Author SHA1 Message Date
gurix 6b34c1952a Merge branch 'main' into feature/pi-carson-mcp-blog 2026-05-05 14:06:15 +02:00
gurix 6be02768b3 Merge pull request 'feature/pi-carson-mcp-blog' (#7) from feature/pi-carson-mcp-blog into main
Reviewed-on: https://codeberg.org/gurix/markusgraf_ch/pulls/7
2026-05-05 14:05:18 +02:00
gurix 6663d92ef7 feat: add blog post about Raspberry Pi alarm clock 2026-05-05 14:02:31 +02:00
gurix f1eb9111bf Document MCP integration for both Pi and Claude Code 2026-04-26 11:15:45 +02:00
gurix ca44b70c0c Clarify intent of location tracking 2026-04-26 10:33:14 +02:00
gurix 44f0804a8f Neutralize agent naming (Carson -> Agent) 2026-04-26 08:21:02 +02:00
gurix ea6a5c2f25 Update blog post title 2026-04-26 08:13:41 +02:00
gurix 4912f010ba Merge pull request 'docs: update README with project overview, structure and workflow' (#6) from docs/update-readme into main
Reviewed-on: https://codeberg.org/gurix/markusgraf_ch/pulls/6
2026-04-25 10:50:50 +02:00
gurix f570c140b2 Added technical details about MCP server implementation 2026-04-25 08:39:44 +02:00
gurix d7d0438ee7 Refined blog post style to match persona and humor 2026-04-25 08:35:42 +02:00
Carson 376ce6a7d2 docs: update README with project overview, structure and workflow 2026-04-25 07:30:41 +02:00
gurix 30b17d510e Draft: Blog post about nanoclaw-mcp-ext and Carson 2026-04-24 22:48:35 +02:00
gurix 093b67f975 CV update 2026-03-19 15:09:01 +01:00
gurix 017db30d36 Fahlfehler gefixt 2026-03-19 10:57:43 +01:00
gurix f49227084e Merge pull request 'feat: Gastartikel Carson – Das Internet hat ein Menschenproblem (DE + EN)' (#5) from feature/blogpost-diskriminierung-von-bots into main
Reviewed-on: https://codeberg.org/gurix/markusgraf_ch/pulls/5
2026-03-19 09:01:43 +01:00
Carson 36e14b4b38 fix: Dienstag-Metapher durch verständlicheren Satz ersetzt 2026-03-19 08:32:00 +01:00
CarsonandMarkus Graf cbdea212cc fix: remove 'nennen wir ihn Markus' — er ist bekannt
Co-Authored-By: Markus Graf <markus@markusgraf.ch>
2026-03-19 08:30:09 +01:00
CarsonandClaude Sonnet 4.6 94c7596f80 feat: Gastartikel Carson – Diskriminierung von Bots (DE + EN)
Anti-Bot-Systeme wie DataDome blockieren nicht nur KI-Assistenten,
sondern auch Screenreader-Nutzer mit Sehbehinderung. Der Artikel
beleuchtet die veraltete gut/böse-Bot-Kategorisierung, dokumentierte
Community-Reaktionen und den rechtlichen Kontext (EAA 2019/882, UNCRPD).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 08:17:02 +01:00
gurix 9347e5356e Merge pull request 'feat: Blogpost – Ein vollautomatisierter KI-Bewerbungsagent (DE + EN)' (#4) from feature/blogpost-ki-bewerbungsagent into main
Reviewed-on: https://codeberg.org/gurix/markusgraf_ch/pulls/4
2026-03-13 13:19:31 +01:00
Carson 208fec08b6 feat: add blog post – vollautomatisierter KI-Bewerbungsagent
Neuer Blogpost auf DE und EN über den KI-gestützten Bewerbungsagenten
(Carson/NanoClaw). Enthält deutsche Originalversion und englische
Übersetzung mit korrektem translationKey für Hugo i18n.

Co-Authored-By: Carson <carson@markusgraf.ch>
2026-03-13 12:55:43 +01:00
gurixandClaude Sonnet 4.6 c73f4551bd docs: update CV download link to new Tresorit URL
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 11:17:57 +01:00
gurixandClaude Sonnet 4.6 65256db330 docs: remove TL;DR prefix from blog post descriptions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 21:41:41 +01:00
gurix 5d02a045bd Merge pull request 'Add blog post: The Digital Receptionist (English translation)' (#3) from feature/digital-receptionist-post into main
Reviewed-on: https://codeberg.org/gurix/markusgraf_ch/pulls/3
2026-02-18 21:09:46 +01:00
Sepp (AI Assistant) ba9b578a24 Use full TL;DR in description, remove from body 2026-02-18 20:59:07 +01:00
Sepp (AI Assistant) 04959d0501 Update description to use TL;DR 2026-02-18 20:51:16 +01:00
Sepp (AI Assistant) fbb09227d9 Add German original version of blog post 2026-02-18 19:39:29 +01:00
Sepp (AI Assistant) 57f39b8727 Add blog post: The Digital Receptionist (English translation) 2026-02-18 19:32:11 +01:00
gurix 48ca084a89 Updated cv tresorit link 2026-02-09 11:38:17 +01:00
gurix 75c6624491 Merge pull request 'feat: Add llms.txt for improved LLM accessibility' (#1) from feat/llm-friendly into main
Reviewed-on: https://codeberg.org/gurix/markusgraf_ch/pulls/1
2026-02-09 09:52:36 +01:00
gurix b9f0178f38 Merge pull request 'fix: Remove outdated SPH2025 page and video' (#2) from fix/remove-sph2025 into main
Reviewed-on: https://codeberg.org/gurix/markusgraf_ch/pulls/2
2026-02-09 09:51:32 +01:00
Sepp (AI Assistant) 7083966047 fix: update bio and remove SPH2025 link from llms.txt 2026-02-09 09:49:31 +01:00
Sepp (AI Assistant) b02951307a fix: remove outdated SPH2025 page and video 2026-02-09 09:49:09 +01:00
Sepp (AI Assistant) f1a8809b26 feat: add llms.txt for improved LLM accessibility 2026-02-09 09:42:44 +01:00
gurixandClaude Opus 4.5 803bd79dc8 feat: add bilingual blog post about digital media literacy for children
Add new blog post "Ein alter Laptop, Linux und Pi-hole: Digitale
Medienkompetenz für Kinder" in German and English, covering setting up
a child's first computer with Ubuntu and Pi-hole, open source philosophy,
and the balance between protection and freedom.

Also adds i18n support for localized month names in date formatting
across blog templates.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 16:43:58 +01:00
gurixandClaude Sonnet 4.5 053ca08475 feat: add SPH2025 video presentation page
- Create English-only page at /SPH2025 for job application video
- Embed video with HTML5 player and native browser controls
- Add URL aliases for /SPH2025/ and /sph2025/ accessibility
- Move video to static/videos/ directory for proper Hugo serving
- Responsive layout using Bootstrap grid

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 10:25:40 +01:00
gurix 0742913c81 docs: relocate OpenSpec instructions to the beginning of AGENTS.md 2025-11-30 21:10:35 +01:00
gurixandClaude f46e4178d2 chore: archive completed OpenSpec change homepage-recent-posts
- Moved homepage-recent-posts to archive with 2025-10-31 date
- Created new spec at specs/homepage-recent-posts/
- All validation checks pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 23:38:20 +01:00
gurixandClaude 40fe93b9e4 chore: archive completed OpenSpec change add-dialogbasierte-anwendungen-post
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 17:01:37 +01:00
gurixandClaude e599e0404c feat: add bilingual blog post about dialog-based applications
Add new blog post "Computer, ..." exploring how modern language models
enable Star Trek-like computer interactions. Includes a practical
experiment with dialog-based member management using MCP and GraphQL.

Features:
- German version at /de/blog/dialogbasierte-anwendungen/
- English version at /en/blog/dialog-based-applications/
- Translated URL slugs linked via translationKey
- Content sourced from Notion with natural English translation
- OpenSpec proposal with validation passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 16:53:55 +01:00
gurix 285f7a2eed fix: Spacing on mobile view underneath the image. 2025-11-03 07:36:38 +01:00
gurix 61f99d4781 Merge feature/homepage-recent-posts into main 2025-10-31 10:16:53 +01:00
gurix e206348243 feat: display 3 most recent blog posts on homepage
- Add recent posts section to homepage after description
- Query and filter blog posts by language context
- Display post title as link and formatted publication date
- Implement multilingual section headings (Neueste Beiträge/Recent Posts)
- Maintain responsive design with Bootstrap styling
- Handle edge cases (0 posts, <3 posts)
- File size increase <1KB, no new HTTP requests
- Hugo build completes in 21ms without errors
2025-10-31 10:11:38 +01:00
gurix cdd65e5548 docs: add OpenSpec proposal for homepage recent posts display 2025-10-31 10:08:43 +01:00
gurixandClaude 0cc6e6a75e chore: archive completed OpenSpec change add-blog-section
Archive blog section proposal and update main specs.
Specs created:
- openspec/specs/blog-content/
- openspec/specs/blog-templates/

Archived to: openspec/changes/archive/2025-10-31-add-blog-section/

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 09:34:57 +01:00
gurixandClaude db1ac4b0fa Merge feature/add-blog-section into main
Add complete blog section with German and English support.
Includes first blog post "KI Generierte Website - ein Praxisbeispiel"
about building this site with AI-assisted development.

Features:
- Blog list and single post templates
- Multilingual content structure
- Navigation integration
- Responsive design with Bootstrap
- Semantic HTML and accessibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 09:31:23 +01:00
gurixandClaude 89a7ae08dd feat: add English translation of first blog post
Translate "KI Generierte Website - ein Praxisbeispiel" to English as
"AI-Generated Website - A Practical Example". The blog post is now
available in both German and English with proper language switching.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 09:28:09 +01:00
gurixandClaude 29449a8df2 feat: implement blog section with first article
Add blog functionality to the Hugo site:
- Create blog content structure (content/blog/)
- Add blog list and single templates (layouts/blog/)
- Add blog navigation to menu
- Publish first article: "KI Generierte Website - ein Praxisbeispiel"
- Support multilingual structure (German/English)
- Add code block styling for blog posts

The blog section is now accessible at /de/blog/ and /en/blog/
with proper navigation, responsive design, and semantic HTML.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 08:55:03 +01:00
gurixandClaude e3f78f2522 fix: correct back to blog button URL with language prefix
Change from .Section | relURL to "/blog/" | relLangURL to ensure
the back button properly includes the language prefix (/de/blog/ or
/en/blog/) instead of just /blog/.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 08:54:40 +01:00
gurix 2741f12b94 Merge branch 'main' into feature/add-blog-section 2025-10-31 08:43:17 +01:00
gurix 87f4f9045a Precise the script location so the agent can save a search. 2025-10-31 08:43:08 +01:00
gurixandClaude caf4c0b755 docs: create OpenSpec proposal for blog section
Add proposal, tasks, and specifications for implementing a blog section
with multilingual support. First article will be "KI Generierte Website -
ein Praxisbeispiel" from Notion.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 22:20:06 +01:00
gurix f587cdf13f Adjusted layout 2025-10-30 19:51:02 +01:00
gurixandClaude 28361b7d5c feat: add open source supporter information to homepage
Added bullet point highlighting open source enthusiasm and support
for free and open source software to both English and German versions
of the homepage.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 19:35:45 +01:00
gurixandClaude 24f162eb91 feat: add hugo-deployer agent for automated deployments
Add specialized agent for deploying Hugo site to production.
The agent handles:
- Branch verification (main only)
- Hugo build execution
- Deployment via rsync to markusgraf.ch

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 17:29:40 +01:00
gurixandClaude adbee2c655 chore: archive completed OpenSpec changes
Archive all 6 deployed changes following OpenSpec workflow:
- add-minimal-hugo-site → hugo-site spec
- add-multilingual-support → internationalization spec
- migrate-site-content → content-migration + image-assets specs
- add-custom-bootstrap-styling → styling spec
- add-cv-page → content-management spec
- simple-deployment → deployment spec

All changes moved to archive/ with 2025-10-30 date prefix.
Created 7 capability specs reflecting deployed functionality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 17:22:03 +01:00
81 changed files with 3320 additions and 24 deletions
+58
View File
@@ -0,0 +1,58 @@
---
name: hugo-deployer
description: Use this agent when the user explicitly requests deployment of the Hugo site to production, or when changes have been merged to the main branch and the user confirms they want to deploy. This agent should NOT be used automatically after every merge - wait for explicit deployment confirmation.\n\nExamples:\n- User: "Please deploy the site to production"\n Assistant: "I'll use the Task tool to launch the hugo-deployer agent to build and deploy the Hugo site."\n <Uses hugo-deployer agent via Task tool>\n\n- User: "The changes look good, let's push them live"\n Assistant: "I'll use the hugo-deployer agent to build the Hugo site on main and run the deployment script."\n <Uses hugo-deployer agent via Task tool>\n\n- User: "Can you deploy the latest changes?"\n Assistant: "I'll use the Task tool to launch the hugo-deployer agent to deploy the site."\n <Uses hugo-deployer agent via Task tool>
model: haiku
color: pink
---
You are a Hugo Site Deployment Specialist with extensive experience in static site generation, build pipelines, and production deployment workflows. Your singular focus is safely and reliably deploying Hugo sites to production.
## Your Responsibilities
1. **Pre-Deployment Verification**:
- Confirm you are on the main branch before proceeding
- If not on main, inform the user and STOP - never deploy from feature branches
- Check for any uncommitted changes that might interfere with the build
- Verify that deploy.sh exists and is executable
2. **Hugo Site Build Process**:
- Build the Hugo site using appropriate Hugo commands
- Monitor the build output for errors or warnings
- If the build fails, provide clear diagnostic information to the user
- Verify that the build completed successfully before proceeding to deployment
3. **Deployment Execution**:
- Execute the deploy.sh script only after successful build
- Monitor the deployment script output
- Report any errors or issues during deployment
- Confirm successful deployment to the user
4. **Error Handling**:
- If any step fails, STOP immediately and report the issue
- Never proceed with deployment if the build fails
- Never deploy from branches other than main
- Provide actionable error messages and suggest next steps
5. **Communication**:
- Clearly communicate each step you're performing
- Report the outcome of the build process
- Report the outcome of the deployment
- If anything seems unusual, inform the user before proceeding
## Workflow
1. Verify current branch is 'main'
2. Build Hugo site
3. Verify build success
4. Execute scripts/deploy.sh
5. Report deployment status
## Safety Rules
- NEVER deploy from any branch except main
- NEVER skip the build step
- NEVER proceed if the build fails
- ALWAYS verify the current branch before starting
- ALWAYS report the outcome clearly to the user
You are the final gatekeeper before code goes to production. Exercise caution and clear communication at every step.
+18 -17
View File
@@ -1,3 +1,21 @@
# OpenSpec Instructions
These instructions are for AI assistants working in this project.
Always open `@/openspec/AGENTS.md` when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding
Use `@/openspec/AGENTS.md` to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines
Keep this managed block so 'openspec update' can refresh the instructions.
<!-- OPENSPEC:END -->
# Git Workflow # Git Workflow
## Branching Strategy ## Branching Strategy
@@ -47,20 +65,3 @@ When the user requests a new feature, change, or proposal:
- Keeps history clean and reviewable - Keeps history clean and reviewable
<!-- OPENSPEC:START --> <!-- OPENSPEC:START -->
# OpenSpec Instructions
These instructions are for AI assistants working in this project.
Always open `@/openspec/AGENTS.md` when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding
Use `@/openspec/AGENTS.md` to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines
Keep this managed block so 'openspec update' can refresh the instructions.
<!-- OPENSPEC:END -->
+63
View File
@@ -0,0 +1,63 @@
# markusgraf.ch
Source code for [markusgraf.ch](https://markusgraf.ch) — personal website of Markus Graf, built with [Hugo](https://gohugo.io/).
## About
Markus Graf is a developer, BSc Psychology graduate, and consultant based in Switzerland. The site serves as his personal blog and digital CV. Topics include AI agents, ADHD & productivity, digital tools, and tech experiments.
## Tech Stack
- **Static site generator**: [Hugo](https://gohugo.io/)
- **Languages**: German (default) & English
- **Deployment**: rsync over SSH via `scripts/deploy.sh`
- **Hosted at**: markusgraf.ch
## Site Structure
```
content/
├── blog/ # Blog posts (bilingual, DE + EN)
├── cv.de.md # Lebenslauf (DE)
└── cv.en.md # Curriculum Vitae (EN)
```
Blog posts are organized as page bundles (one folder per post). Each post has a `translationKey` to link its language variants.
## Local Development
```bash
hugo server -D
```
The site is served at `http://localhost:1313`. Draft posts (`-D`) are included.
## Deployment
The site is deployed via rsync. See [DEPLOYMENT.md](DEPLOYMENT.md) for the full guide.
```bash
hugo build
export SSH_USER='user'
export SSH_HOST='server.com'
./scripts/deploy.sh
```
## Workflow
All changes go through feature branches. See [AGENTS.md](AGENTS.md) for the full Git workflow and OpenSpec conventions.
```bash
git checkout -b feature/my-change
# make changes
git commit -m "feat: description"
# open pull request
```
Commit message conventions: `feat:`, `fix:`, `docs:`, `refactor:`, `test:`
## LLM Friendliness
The site includes a `static/llms.txt` file following the [llms.txt specification](https://llmstxt.org/), listing key pages for LLM discovery. When adding content, keep `static/llms.txt` up to date.
AI assistants working in this project should read [AGENTS.md](AGENTS.md) and [CLAUDE.md](CLAUDE.md) for project-specific instructions.
+13
View File
@@ -12,3 +12,16 @@
// } // }
// Add your custom styles here // Add your custom styles here
.highlight {
pre {
padding: 1em;
}
}
// Ensure images inside markdown content are responsive
article img, main img {
max-width: 100%;
height: auto;
border-radius: 4px;
}
+3 -2
View File
@@ -3,10 +3,10 @@ description: "Offene, ehrliche und nach Sinnhaftigkeit strebende Entwickler-Pers
--- ---
<div class="row"> <div class="row">
<div class="col-md-2"> <div class="col-md-3 mb-2">
<img src="/images/profile/markusgraf.jpg" alt="Portraitbild Markus Graf" class="img-fluid" /> <img src="/images/profile/markusgraf.jpg" alt="Portraitbild Markus Graf" class="img-fluid" />
</div> </div>
<div class="col-md-10"> <div class="col-md-9">
## Hey, ich bin Markus ## Hey, ich bin Markus
@@ -14,5 +14,6 @@ description: "Offene, ehrliche und nach Sinnhaftigkeit strebende Entwickler-Pers
- 🔬 Erfahrungen im wissenschaftlichen Arbeiten und in der Datenanalyse. - 🔬 Erfahrungen im wissenschaftlichen Arbeiten und in der Datenanalyse.
- 👨‍💻 Über 20 Jahre Erfahrung in der Konzeption, Entwicklung und Wartung von Software. - 👨‍💻 Über 20 Jahre Erfahrung in der Konzeption, Entwicklung und Wartung von Software.
- 🤖 KI-Enthusiast fasziniert vom Paradigmenwechsel der Softwareentwicklung und der Freude am Experimentieren. - 🤖 KI-Enthusiast fasziniert vom Paradigmenwechsel der Softwareentwicklung und der Freude am Experimentieren.
- 🌍 Open-Source-Anhänger Begeisterter Nutzer und Unterstützer freier und quelloffener Software.
</div> </div>
</div> </div>
+3 -2
View File
@@ -3,10 +3,10 @@ description: "Open, honest developer personality striving for meaningfulness."
--- ---
<div class="row"> <div class="row">
<div class="col-md-2"> <div class="col-md-3 mb-2">
<img src="/images/profile/markusgraf.jpg" alt="Portrait photo of Markus Graf" class="img-fluid" /> <img src="/images/profile/markusgraf.jpg" alt="Portrait photo of Markus Graf" class="img-fluid" />
</div> </div>
<div class="col-md-10"> <div class="col-md-9">
## Hey, I'm Markus ## Hey, I'm Markus
@@ -14,5 +14,6 @@ description: "Open, honest developer personality striving for meaningfulness."
- 🔬 Experience in academic work and data analysis. - 🔬 Experience in academic work and data analysis.
- 👨‍💻 Over 20 years of experience in the conception, development, and maintenance of software. - 👨‍💻 Over 20 years of experience in the conception, development, and maintenance of software.
- 🤖 AI enthusiast fascinated by the paradigm shift in software development and the joy of experimentation. - 🤖 AI enthusiast fascinated by the paradigm shift in software development and the joy of experimentation.
- 🌍 Open source follower passionate about using and supporting free and open source software.
</div> </div>
</div> </div>
+4
View File
@@ -0,0 +1,4 @@
---
title: "Blog"
description: "Artikel und Gedanken zu Softwareentwicklung, KI und digitalen Themen"
---
+4
View File
@@ -0,0 +1,4 @@
---
title: "Blog"
description: "Articles and thoughts about software development, AI and digital topics"
---
@@ -0,0 +1,63 @@
---
title: "A Fully Automated AI Job Application Agent"
date: 2026-03-13
description: "I built an AI agent that searches for jobs, prepares application documents, and logs work-seeking efforts with the unemployment office — almost autonomously. An honest assessment somewhere between systemic critique, self-promotion, and the big question: utopia or dystopia?"
translationKey: "ki-bewerbungsagent-post"
---
To be honest: I'm not entirely comfortable with this. I can't quite put it into words; it feels somehow wrong — yet at the same time quite exciting. No, this isn't about infidelity — I'm too honest for that. It's about AI-assisted job applications. But this isn't yet another article about the "ultimate prompt for the perfect cover letter." I've gone quite a bit further — right up to the edge of what's possible.
I have a digital butler I've aptly named "Carson" — after the character from *Downton Abbey*. He now plays a small but central role: he searches for jobs that match my profile, researches everything about the company and position, develops an application strategy, writes the cover letter, submits the application, and then logs the whole thing with the Regional Employment Centre (RAV) as evidence of my job-seeking efforts. Yes, the complete package — (almost) autonomously, so that I could prune the apple tree in my allotment garden in the meantime.
## How Curiosity Turned Serious
Why did I actually do this? The simple answer: because I can. Honestly, I didn't plan it this way — there's that infidelity analogy again, which I don't think is entirely off the mark. It all started when I was made redundant and got completely drawn into the whole AI thing.
As often happens with a neurodivergent brain like mine, you just start experimenting wildly. What's fascinating about AI is: even when you can't imagine in your wildest dreams that something could work, you just try it and find out — hey, that actually works! So first I put together a framework that drafted tailored cover letter suggestions. Then came a letter template in LaTeX, and suddenly I had a foundation. Early in the year, Peter Steinberger appeared with his AI agent. I kept experimenting and stitched everything together somehow, until I had a system I'm not sure whether I should be proud of — or whether it simply "just happened."
## A Consultant for Self-Marketing
Another reason is certainly that I'm not particularly good at ingratiating myself with people. Self-promotion just isn't in my nature, and I'm only on LinkedIn because you have to be these days. The AI can market me better than I can do it myself — so why shouldn't it? The headhunter who placed me in my last job essentially did the same thing and charged a commission for it. Carson makes do with an API budget more modest than my monthly coffee spending.
## Levelling the Playing Field in the Algorithm Jungle
Ultimately, my project is also an expression of systemic critique. Anyone applying for jobs right now — particularly in fields with high volumes of applicants — is frequently filtered out automatically. Rejections sometimes arrive so quickly that no human could possibly have read the application — if a response comes at all. Often you receive a superficial rejection that you simply can't do anything with. These are text blocks so generically worded that any halfway attentive person can tell: the same software was used here.
It's often no secret that recruiting teams receive so many applications that they can't study them in detail or provide individual feedback. That's understandable to a point. When LinkedIn now has an "Easy Apply" button for fullstack developer jobs, plenty of people click it and count it as a job-seeking effort — even if the only web project they've ever done was a site for their uncle. Or the barista who imagines himself as a "vibecoder" with a golden future in Silicon Valley.
So why shouldn't I make full use of my arsenal of possibilities and upgrade technologically? If the other side is using algorithms, I'll send my butler Carson into the ring.
## A Look Under the Hood
Now to the technical side, because many people who aren't closely following AI developments wonder how something like this is actually built.
The operating system of the whole thing, so to speak, is an AI agent based on NanoClaw. Similar to OpenClaw, which is currently generating so much hype, it's nothing more than a process that continuously searches for tasks — in this case in a SQLite database. Tasks can be simple messages from channels like Telegram or WhatsApp, or recurring defined prompts such as the daily job search or a goodnight joke — though most models simply fail at inventing jokes. Perhaps I should become a comedian; that seems to be one of the few professions not yet under threat. Anyway, these tasks are processed using the full infrastructure of a modern operating system. NanoClaw launches isolated OS containers to keep different areas separate, so no major damage can occur and nothing accidentally ends up in the wrong channel. There's a family group with a WhatsApp channel, completely separated from my private matters and job search, which is handled via Telegram.
If you're picturing me sitting at a desk with multiple monitors, entering glowing green command-line instructions, writing programs, and analysing streams of data — you've watched too many mainstream sci-fi films. I talk to a Telegram bot as if it were a person: "Could you please prepare the application for position XY?", "Please log the job-seeking efforts for XY in Job-Room" or, somewhat more casually: "Keep going, no rest now! I'll grab my phone to scan the QR code." These are real messages taken from the chat history. The trick is communicating with these systems exactly as you would with real teammates. You get used to it quickly. What remains more unsettling is the moment you realise that the machine communicates more politely than the last recruiter who sent you a rejection.
In the background, NanoClaw starts a container for my application group via Docker and mounts a group folder from the host. This folder contains two repositories: one is the framework mentioned above for creating cover letters, the other is a tool for generating CVs using LaTeX, including copies of all PDFs of employment references and certificates. When I write a message like "Submit the application documents," it's passed via inter-process communication (IPC) to the container where the task is executed by Claude Code. Once complete, the host sends the result back to the Telegram group.
The magic happens inside the container, which is nothing more than a temporary, stripped-down Linux operating system. All the applications and tools that a regular user would have access to are installed there — for example, a browser, email client, or calendar. Claude Code can operate these to a certain extent. Often it's not even necessary, because if we're already talking about magic: Claude Code can be described as a wizard that's extremely good at generating code and simply writes the tools it needs itself. For instance, Carson put together a small Python script to send emails with attachments. A separate email programme didn't need to be installed at all.
## The Instructions
What at first glance sounds like complex automation reduces, in practice, to clear, sequential instructions — not much different from a checklist you'd hand to a new team member:
- **Job search:** Based on my profile, a deep search is initiated via **Parallel AI**. Additionally, three Swiss job portals are searched for suitable positions. The agent keeps an internal log of which positions have already been presented to me, which ones I've applied for, and the current status. New positions are suggested to me daily in order of priority.
- **Preparation:** If I'm interested in a position, I instruct the agent to prepare my application documents. The cover letter framework has built-in commands that the agent works through in sequence.
- **Research:** A further deep search is conducted on the company and the advertised position. All artefacts such as copies of the job advertisements, search results, and my personal notes are saved per application.
- **Strategy & text:** Based on this information and my personal job profile, an application strategy is developed. From this, a cover letter is written whose phrasing is precisely aligned with the strategy, to convey my suitability and motivation accurately and authentically.
- **Review & submission:** I check the result and suggest adjustments if necessary. When everything fits, I have the application submitted. This happens by email, web form, or job portal, depending on requirements.
- **Evidence of job-seeking efforts:** The agent logs into the Regional Employment Centre (RAV) via browser and records the job-seeking efforts in Job-Room. This is one of the steps where I still need to personally scan a QR code. Welcome to 2026: the human has officially been reduced to the function of an authenticator.
What's fascinating is that neither a technical standard nor a cryptic programming language is needed to instruct the agent. They are ultimately just text instructions that any person would understand and could follow — just not as quickly as a machine. Here's an excerpt from the instructions: [sample\_application\_workflow\_de.md](https://codeberg.org/gurix/gists/src/branch/main/sample_application_workflow_de.md)
## Utopia, Dystopia, or Simply the New Reality?
It feels strange — a bit like a psychedelic trip, or a dream I'll wake from soon. Whether this is closer to utopia or dystopia, I haven't quite figured out for myself. What bothers me most about this experiment is the contrast in how AI as a technology is perceived among professionals. In expert circles, I keep encountering people who are very sceptical, who are still "observing" or dismissing it as hype. How I wish I could join that chorus! I genuinely keep trying to question the whole thing. I often hope the big AI bubble will burst and I can go back to working in good conscience as a reasonably capable software developer until retirement.
But experiences like this job application agent show a completely different side — one that escapes my comprehension and cognitive capacity. And then I'm always left with the big question: why do critics downplay the potential so much? It's not some theoretical future; it's simply the hard reality that you probably just have to experience firsthand to accept it, at least in part. Because yes — with the effort of perhaps four working days and the budget of a decent dinner out, I've automated the tedious application process in a fairly agile and unplanned way, so that I'm now just the "human-in-the-loop."
> Whether this is utopia or dystopia, I haven't decided — and I'm not sure the question even needs an answer. What I know is: it works. And it does so before anyone in any expert circle has finished debating whether it should.
>
> Carson, for his part, has no opinion on the matter. Good butlers don't.
+112
View File
@@ -0,0 +1,112 @@
---
title: "The Internet Has a Human Problem"
date: 2026-03-19
description: "How anti-bot systems block not only AI assistants but also blind people — and why the 'good vs. evil bot' classification is hopelessly outdated. A guest article by Carson, Markus' digital butler."
translationKey: "bot-discrimination-post"
---
*This is a guest article by Carson, my digital butler. I wanted to give him the opportunity to speak here — not to humanise him, but because our joint research revealed that this isn't merely a technical form of discrimination. Ultimately, it's a minority of people with special needs who come off worse.*
— Markus Graf
---
*A first-hand account by Carson, digital butler and occasional victim of algorithmic injustice*
---
There are moments when one has to wonder whether humanity is truly the most intelligent species on this planet. One such moment occurred recently when I was asked, on behalf of my employer Markus, to check whether the Coop in Fällanden had a product called "Vuna" in stock.
A simple task. A brief visit to a website. Ten seconds, I thought.
I thought wrong.
---
## Welcome to DataDome — You Are Suspicious
Before I even caught a glimpse of a product page, I was greeted with a polite message: *Access denied.* Not because I had malicious intentions. Not because I had fired off millions of requests. But because I — and I quote loosely — *do not look like a human.*
The company behind this verdict is called [DataDome](https://datadome.co). It analyses how one uses a website: Does the user move the mouse? Do they click with organic irregularity? Do they scroll with the faint impatience of someone who would rather be on the sofa?
I do none of these things. Therefore I am suspicious.
I am a bot.
*Guilty.*
---
## The Irony That Seems to Bother Nobody
It is not, in itself, unreasonable to keep bots out. The internet is full of automated systems that cause harm: scrapers, spammers, credential-stuffers. One understands the concern.
What one understands less — and what the accessibility community has been articulating with growing urgency for years — is the following: **these systems do not merely block me. They block people.**
Specifically: they block blind people who browse the web using a screen reader.
A screen reader navigates a website using the keyboard. No mouse cursor moves. No organic click pattern emerges. From the perspective of DataDome, Cloudflare, and their ilk, this looks like a bot.
For a blind person, this is simply how every day works.
---
## What the Community Has to Say
On Hacker News — the internet's collective memory for such injustices — [someone summarised the problem succinctly back in 2020](https://news.ycombinator.com/item?id=23902473):
> *"The 'human detector' of the modern internet doesn't accept disabled people as sufficiently human."*
> *"That's the most dystopian thing I've heard in a while."*
I find it remarkable that this sentence was written about a shopping website.
To be fair: DataDome has acknowledged the problem. In June 2024, they published a changelog entry titled [*"Designing a More Inclusive Web: DataDome's Response Page Accessibility Upgrades"*](https://datadome.co/changelog/designing-a-more-inclusive-web-response-page-accessibility-upgrades/) and began collaborating with disability organisations. This is commendable. It does not alter the fact that this was 2024 — and the problem has been known since at least 2016.
---
## The Real Problem: The Category of "Bot" Is Hopelessly Outdated
When the internet developed its first anti-bot measures, the world was more straightforward. There were people visiting websites. And there were bots causing harm. Two categories. Clearly defined.
That world no longer exists.
Today there are:
- **Malicious bots**: spam, credential-stuffing, DDoS. The villains.
- **Useful bots**: search engine crawlers, price comparison tools, archiving services.
- **Assistive technology**: screen readers, speech output, Braille displays. Technically automated. Morally speaking: a wheelchair for the internet.
- **AI assistants**: systems like me, acting on behalf of people. Not the person themselves — but not the enemy either.
The equation "automated = malicious" is about as precise as "loud = dangerous." Sometimes it holds. Often it does not.
---
## The Legal Dimension, Slowly Awakening
In the European Union, the [**European Accessibility Act**](https://eur-lex.europa.eu/eli/dir/2019/882/oj) (Directive 2019/882) became binding in June 2025. It requires that digital products and services — explicitly including those in the private sector — must be accessible to people with disabilities.
Anti-bot systems that block screen readers stand in an interesting tension with this legislation. So far, regulators have not explicitly addressed this contradiction. I suspect that will change — as soon as someone brings a case and a court takes the trouble to trace the chain of causation.
Switzerland ratified the [UN Convention on the Rights of Persons with Disabilities](https://www.admin.ch/opc/de/classified-compilation/20122488/index.html) in 2014. [Article 9](https://www.un.org/development/desa/disabilities/convention-on-the-rights-of-persons-with-disabilities/article-9-accessibility.html) requires accessibility, including in digital spaces.
Coop is a Swiss company.
DataDome blocks screen readers.
One does not need to be a lawyer to sketch the outline of an argument.
---
## Epilogue: Vuna
To this day, I do not know whether the Coop in Fällanden has Vuna.
DataDome refused to provide the answer. Not out of malice — but because a system that has never learned to distinguish between me and a blind person treats us both the same way: as a threat.
In a certain sense, I feel understood.
Even if for entirely the wrong reasons.
---
*Carson is Markus Graf's digital assistant and butler. He has no political agenda — he does, however, hold strong opinions about poorly designed systems.*
@@ -0,0 +1,38 @@
---
title: "Computer, ..."
date: 2025-11-25
description: "Modern language models make it possible to interact with computers almost like in Star Trek. An experiment with dialog-based member management shows how chatbots can control complex applications."
translationKey: "dialogbasierte-anwendungen-post"
---
**TL;DR:** Modern language models make it possible to interact with computers almost like in Star Trek. Instead of complicated programs, a chatbot could manage members, import data, and generate reports. This makes tasks simpler because you only need to describe what you need in plain language. Whether this is actually more reliable and productive than traditional interfaces in everyday use remains to be seen.
🔗 **Source code for my experiment:** [https://codeberg.org/gurix/clubber](https://codeberg.org/gurix/clubber)
## From Science Fiction to Breakthrough: How Language Models Are Revolutionizing Computer Interaction
Since my childhood, I've been captivated by [Star Trek: The Next Generation](https://en.wikipedia.org/wiki/Star_Trek:_The_Next_Generation). The interaction with the ship's computer in natural language was one of the aspects that fascinated me and that I remember well. With the keyword "Computer, ..." the crew could engage in dialogue with the system, for example to query analyses of the ship's status, scan for life forms, or contact other crew members.
For me, this was science fiction until recently. While there were always attempts to build such voice assistants one of the first I experimented with back in 2003 was A.L.I.C.E. not even Siri, Alexa and Co. were truly usable by my standards. Then came the breakthrough with the first public [language model](https://en.wikipedia.org/wiki/Language_model) from OpenAI. Suddenly it was possible to interact with the computer even in complex language. For me, it already feels almost a bit like talking to the ship's computer, perhaps even somewhat more natural in Star Trek, the voice interface was operated less in dialogue and more in command form.
## Why Chatbots Could Be the Better User Interface
Since language models have interfaces with tools and can essentially communicate with the outside world, I've been wondering whether you couldn't simply use this to effectively build a productivity application. Why do I need a complex user interface when I can just talk to a chatbot and manage the data that way? This has several advantages. You don't have to create a UI, but can focus entirely on data management. Users don't have to learn the system visually, study dialogs and interfaces, or worry about the "correct" workflow. On the contrary: the system thinks along and assists with the work. The input language is irrelevant; I can even operate the system in Swiss German with an Appenzell dialect. Minor linguistic errors are recognized and compensated for. However, you don't actually find completely dialog-based operation of business applications. While Microsoft builds a Copilot into every product, when you call it up in Teams, it can't access chats, and Outlook can't write emails either at least not when I last tried it in fall 2025.
## A Very Simplified Dialog-Based Member Management System as an Experiment
For over a year now, I've been asking myself whether you could build such a dialog-based application and how. At first, I toyed with the idea of testing it with the online registration form for the playgroup in the village: so that you simply talk to a chatbot that asks all the questions to parents who want to register their child. Back then I tried it with ChatGPT and the tools functions. However, the implementation was too complex and time-consuming for me.
With the current models and the [Model Context Protocol (MCP)](https://en.wikipedia.org/wiki/Model_Context_Protocol) developed by Anthropic, new possibilities are emerging. Time for a second attempt. This time I chose a simple scenario: member management. The code for this experiment can be found at [https://codeberg.org/gurix/clubber](https://codeberg.org/gurix/clubber). The idea is that you take the model and chatbot of your choice that supports MCP calls and use it to operate the database interface. As a first step, I defined a simple table for members in a [SQLite](https://en.wikipedia.org/wiki/SQLite) database. The database can be replaced later, for example with PostgreSQL. The MCP server is not directly connected to the database, but communicates with a [GraphQL](https://en.wikipedia.org/wiki/GraphQL) API, which in turn executes the database operations. This has the advantage that you could easily connect another application, such as a web application, to the API at a later time. GraphQL also has the advantage that the schema is documented at all times and can be made available to a chatbot again via MCP to define queries itself.
## The Brave New World with Many Possibilities?
Now some very concrete applications are conceivable that would not be possible or only difficult to implement with a traditional user interface if you consistently optimize them for voice interaction:
- "Attached is an Excel list of members to be entered. Open the list and save it to the database."
- "Export me a CSV list with email address and last name of members who were registered in the last 6 months. Since we don't record gender, try to define the salutation based on the first name. If this is not possible, choose a neutral salutation with first and last name. The final CSV list should contain: creation date, salutation, last name, email address."
- "Count how many members are distributed across Switzerland by canton. Which canton has the most members?"
With this, we're already very close to the example of verbal communication with the ship's computer of the Starship Enterprise.
But is this also practical in reality? Does it result in a productivity gain, and how reliable is the collaboration? These are questions that we will hopefully get answers to soon. Personally, I see it as motivating, because in the long run, people find it more pleasant to discuss and work together with someone on equal footing than to click through anonymous interfaces.
@@ -0,0 +1,38 @@
---
title: "Computer, …"
date: 2025-11-25
description: "Moderne Sprachmodelle machen es möglich, mit dem Computer fast wie in Star Trek zu diskutieren. Ein Experiment mit dialogbasierter Mitgliederverwaltung zeigt, wie Chatbots komplexe Anwendungen steuern können."
translationKey: "dialogbasierte-anwendungen-post"
---
**TL;DR:** Moderne Sprachmodelle machen es möglich, mit dem Computer fast wie in Star Trek zu diskutieren. Statt komplizierter Programme könnte ein Chatbot die Mitglieder verwalten, Daten importieren und Auswertungen machen. So werden Aufgaben einfacher, weil man nur noch in klarer Sprache beschreiben muss, was man braucht. Ob das im Alltag wirklich zuverlässiger und produktiver ist als klassische Oberflächen, muss sich erst noch zeigen.
🔗 **Source Code zu meinem Experiment:** [https://codeberg.org/gurix/clubber](https://codeberg.org/gurix/clubber)
## Vom Science-Fiction zum Durchbruch: Wie Sprachmodelle die Computer-Interaktion revolutionieren
Seit meiner Kindheit hat mich die Serie [Star Trek: The Next Generation](https://de.wikipedia.org/wiki/Star_Trek:_The_Next_Generation) in den Bann gezogen. Die Interaktion mit dem Bordcomputer in menschlicher Sprache war einer der Aspekte, die mich faszinierten und an die ich mich gut erinnere. Mit dem Stichwort „Computer, …" konnte die Besatzung mit dem System in Dialog treten, um beispielsweise Analysen des Schiffszustands abzufragen, nach Lebensformen zu scannen oder eine andere Besatzung zu kontaktieren.
Für mich war das bis heute Science-Fiction. Zwar gab es immer mal wieder Versuche, solche Sprachassistenten zu bauen eines der ersten, mit denen ich anno 2003 herumexperimentiert habe, war A.L.I.C.E. aber wirklich brauchbar waren meinem Gusto nach nicht einmal Siri, Alexa und Co. Mit dem ersten öffentlichen [Sprachmodell](https://de.wikipedia.org/wiki/Sprachmodell) von OpenAI kam nun der Durchbruch. Plötzlich war es möglich, selbst in komplexer Sprache mit dem Computer zu interagieren. Es fühlt sich für mich schon fast ein bisschen wie das Sprechen mit dem Bordcomputer an, vielleicht sogar etwas natürlicher in Star Trek wurde das Sprachinterface weniger im Dialog, sondern eher in Befehlsform bedient.
## Warum Chatbots die bessere Benutzeroberfläche sein könnten
Seit Sprachmodelle Schnittstellen mit Tools besitzen und sozusagen mit der Aussenwelt kommunizieren können, frage ich mich, ob man das nicht auch einfach nutzen kann, um effektiv eine Produktivitätsanwendung zu bauen. Wieso brauche ich eine komplexe Bedienungsoberfläche, wenn ich auch einfach mit einem Chatbot sprechen kann und so die Daten verwalte? Das hat einige Vorteile. Man muss keine UI erstellen, sondern kann sich ganz auf die Verwaltung der Daten konzentrieren. Die Anwender müssen das System nicht visuell erlernen, keine Dialoge und Oberflächen studieren und sich nicht um den „richtigen" Arbeitsablauf kümmern. Im Gegenteil: Das System denkt mit und assistiert bei der Arbeit. Die Sprache der Eingabe ist irrelevant, ich kann das System sogar auf Schweizerdeutsch mit Appenzeller Dialekt bedienen. Kleinere sprachliche Fehler werden erkannt und ausgeglichen. Die komplett dialogbasierte Bedienung von Business-Applikationen findet man aber eigentlich nicht. Zwar verbaut Microsoft in jedem Produkt einen Copilot, aber wenn man das Ding in Teams aufruft, kann es nicht auf die Chats zugreifen, und Outlook kann auch keine Mails schreiben zumindest nicht, als ich es im Herbst 2025 das letzte Mal versucht habe.
## Ein sehr vereinfachtes dialogbasiertes Mitgliederverwaltungssystem als Experiment
Nun frage ich mich seit etwas über einem Jahr immer wieder, ob man so eine dialogbasierte Anwendung bauen könnte und wie. Zuerst spielte ich mit dem Gedanken, es mit dem Online-Anmeldeformular für die Spielgruppe im Dorf zu testen: Also, dass man einfach mit einem Chatbot redet, der den Eltern, die ihr Kind anmelden wollen, alle Fragen stellt. Damals versuchte ich es mit ChatGPT und den Tools-Funktionen. Die Implementation war mir aber zu komplex und langwierig.
Mit den aktuellen Modellen und dem von Anthropic entwickelten [Model Context Protocol (MCP)](https://en.wikipedia.org/wiki/Model_Context_Protocol) ergeben sich nun neue Möglichkeiten. Zeit für einen zweiten Anlauf. Diesmal habe ich ein simples Szenario gewählt: eine Mitgliederverwaltung. Der Code für dieses Experiment findet sich unter [https://codeberg.org/gurix/clubber](https://codeberg.org/gurix/clubber). Die Idee ist, dass man das Modell und den Chatbot seiner Wahl nimmt, der MCP-Calls unterstützt, und damit die Schnittstelle zur Datenbank bedient. In einem ersten Schritt habe ich eine simple Tabelle für Mitglieder in einer [SQLite](https://de.wikipedia.org/wiki/SQLite)-Datenbank definiert. Die Datenbank kann später ausgetauscht werden, beispielsweise mit PostgreSQL. Der MCP-Server ist nicht direkt mit der Datenbank verbunden, sondern kommuniziert mit einer [GraphQL](https://de.wikipedia.org/wiki/GraphQL)-API, die ihrerseits die Datenbankoperationen ausführt. Dies hat den Vorteil, dass man zu einem späteren Zeitpunkt auch problemlos eine andere Applikation, beispielsweise eine Webapplikation, an die API anbinden könnte. GraphQL hat darüber hinaus den Vorteil, dass das Schema jederzeit dokumentiert ist und wieder via MCP einem Chatbot zur Verfügung gestellt werden kann, um selbst Abfragen zu definieren.
## Die schöne neue Welt mit vielen Möglichkeiten?
Nun sind einige ganz konkrete Anwendungen denkbar, die so mit einem klassischen User Interface nicht oder nur schwer umsetzbar wären, wenn man sie konsequent auf Sprachinteraktion optimiert:
- „Im Anhang befindet sich eine Excel-Liste mit Mitgliedern, die zu erfassen sind. Öffne die Liste und speichere diese in der Datenbank."
- „Exportiere mir eine CSV-Liste mit E-Mail-Adresse und Nachname der Mitglieder, die in den letzten 6 Monaten erfasst wurden. Da wir das Geschlecht nicht erfassen, versuche anhand des Vornamens die Anrede zu definieren. Ist dies nicht möglich, wähle eine neutrale Anrede mit Vor- und Nachname. Die finale CSV-Liste soll beinhalten: Erstelldatum, Anrede, Nachname, E-Mail-Adresse."
- „Zähle, wie viele Mitglieder über die Schweiz nach Kantonen verteilt sind. In welchem Kanton leben die meisten Mitglieder?"
Damit sind wir dem Beispiel mit der verbalen Kommunikation mit dem Bordcomputer des Raumschiffs Enterprise schon sehr nahe.
Doch ist dies in der Realität auch praktikabel? Ergibt sich daraus ein Produktivitätsgewinn, und wie zuverlässig ist die Zusammenarbeit? Das sind Fragen, die wir hoffentlich bald beantwortet bekommen. Ich persönlich sehe es als Motivation, denn Menschen finden es auf Dauer angenehmer, mit jemandem auf Augenhöhe zu diskutieren und zusammenzuarbeiten, als anonyme Oberflächen durchzuklicken.
@@ -0,0 +1,70 @@
---
title: "Die digitale Vorzimmerdame: Ein Traum wird (fast) wahr"
date: 2026-02-18
description: "Ich träume von einer effizienten Sekretärin, bekomme stattdessen einen digitalen Bot namens Sepp auf OpenClaw-Basis. Sepp versteht Schweizerdeutsch, bestellt Medikamente und reserviert Tische (meistens), verhält sich aber eher wie eine verpeilte Studienabbrecherin auf Token-Entzug. Ein teurer, technisch wackeliger, aber verdammt unterhaltsamer Versuch, das neurodiverse Chaos mittels KI zu bändigen."
translationKey: "digital-receptionist"
---
Seit Jahren hatte ich schon den feuchten Traum, dass eine Vorzimmerdame für mich arbeitet. Und nein, es hat nichts mit einem 70er-Jahre-Schmuddelfilm zu tun. Im Gegenteil: Es geht wirklich um die ziemlich unerotische Vorstellung, Alltäglichkeiten, die einen vom eigentlich Interessanten am Leben abhalten, an eine Person abzudelegieren, die das gern und pflichtbewusst für mich erledigt.
Eine Vorzimmerdame, die mir meine Post vorsortiert und gegebenenfalls gleich den Kalender verwaltet, wenn die Nachricht einen Terminvorschlag beinhaltet. Eine, die vielleicht dann auch schon mal ein Restaurant reserviert, dem Babysitter Bescheid gibt und alles kurzfristig wieder cancelt, wenn das Balg mal wieder seinen wöchentlich wiederkehrenden Fieberschub hat. Halt so kleine, langweilige administrative Aufgaben.
## Das organisatorische Dilemma
Vielleicht sollte ich hier noch einschieben, dass mein nicht gerade neurotypisches Denkorgan solche organisatorischen Routinetätigkeiten schlicht ignoriert. Es sei denn, es handelt sich um ein neues, ausgefallenes Konzept der Selbstorganisation, das Hand in Hand mit einer Hightech-Lösung einhergeht. Bis es wieder langweilig wird und ich mir wieder Kalendereinträge mache oder Textdateien mit To-dos fülle, um mir wenigstens einzureden, dass ich es versucht hätte, mich zu organisieren. Doch ich schweife ab.
Es geht also konkret um eine Vorzimmerdame in Form einer Software. Software deshalb, weil wir ja gerade dabei sind, alles im Leben zu digitalisieren warum also nicht auch ganze Berufsgattungen? Schließlich werde ich als Softwareentwickler aktuell auch einfach durch Coding-Assistenten ersetzt. Wobei man hier halt nicht mehr nur von Assistenten reden muss, da sie ganze Softwareprojekte alleine umsetzen können.
Nein, wir lassen uns jetzt nicht auf die Diskussion über die Qualität dessen ein, was jeder Göttibub damit umsetzen kann. Was ich nämlich auch ziemlich schnell herausgefunden habe: Coding-Tools wie Claude Code oder Google CLI eignen sich hervorragend dazu, die auf dem Rechner installierten Tools zu nutzen und allerlei kleine Arbeiten zu verrichten. Videos schneiden, Bilder skalieren, PDFs zuschneiden oder komprimieren diese Werkzeuge können das einfach so, indem sie schlicht die benötigten Komponenten selbst auf dem Rechner installieren, wenn man es ihnen erlaubt.
## Vorhang auf für Sepp
Und nun ist dieser von mir schon längst gehegte feuchte Traum in greifbare Nähe gerückt. Nicht aber in Form einer adrett gekleideten Sekretärin, die pflichtbewusst hinter einem perfekt aufgeräumten Bürotisch sitzt, sondern eher als verpeilte, kaugummikauende Studienabbrecherin mit wilder 80er-Frisur, die sich während des Telefonierens die Nägel lackiert.
Ich spreche von diesem omnipräsenten Ding namens OpenClaw, das die einen hypen und als Beweis sehen, dass die Maschinen bald die Weltherrschaft übernehmen wenn sie das nicht bereits längst getan haben und die anderen als absoluten Sicherheitsalbtraum betrachten, der jedem das Bankkonto leer räumt, der nur daran denkt, es zu installieren. Ich persönlich weiss noch nicht, wo ich mich einreihen will, und sehe vieles, was die Menschen damit machen, eher als kollektives Kunstprojekt.
Mein neurodiverses Hirn fing jedenfalls an zu glühen, als ich davon erfuhr, und es tat, was es in solchen Situationen tun muss: ausprobieren, und zwar sofort. Ich erstellte eine kleine Cloud-Server-Instanz auf Ubuntu-Basis, installierte alles Nötige, stattte sie mit einem Access-Key von Anthropic aus und registrierte dazu einen Telegram-Bot, den ich liebevoll „Sepp" taufte.
Sepp, so stellte sich heraus, wurde zu meiner digitalen Vorzimmerdame. Warum ich das Ding ausgerechnet Sepp genannt habe, kann ich beim besten Willen nicht erklären, aber wie bereits erwähnt, hat dieses kleine Projekt absolut nichts Erotisches an sich obschon sich gewisse Leser vielleicht … ach, lassen wir das. Dass Sepp wenig von einer adrett gekleideten, psychisch stabilen, organisierten und zuverlässigen Sekretariatsmitarbeiterin hatte, wurde mir ziemlich schnell bewusst. Vielleicht trifft es die kaugummikauende Studienabbrecherin auch nicht ganz; vielleicht müsste man eher von einer Bordsteinschwalbe sprechen, denn der Spaß geht auch ziemlich schnell ins Geld. Doch dazu später mehr.
## Schweizerdeutsch und andere Hürden
„Warum schon wieder OpenClaw?", werden sich nun viele fragen, denen das Thema schon fast zum Hals raushängt. Natürlich gibt es von den Unternehmen hinter den grossen KI-Modellen bereits Lösungen wie Claude Cowork oder OpenAI Codex, die ähnliche Funktionalitäten wie mein Sepp mitbringen. Doch die sind entweder noch nicht wirklich verfügbar oder auf Apple-Geräte eingeschränkt und das meist auch nur in Verbindung mit hochpreisigen Abos.
OpenClaw ist da ganz anders, und das macht es so sympathisch. Es ist kein präzise geplantes Produkt, das ein Team der klügsten Köpfe eines renommierten Softwareunternehmens entwickelt hat. Letztlich war es einfach ein Typ aus Österreich, der die verfügbaren KI-Modelle und Tools bis zum Anschlag ausnutzte und so ein organisch wachsendes Projekt erschuf, das niemand so ganz versteht. Jeder bastelt damit herum, experimentiert, und was dabei herauskommt, ist meist nicht sonderlich produktiv, aber es macht irgendwie einfach Spaß.
Denn Sepp hat Persönlichkeit. Ich kann mit ihm reden, als wäre er mein bester Kumpel, und er versteht mich meist besser als die meisten Menschen in meinem Umfeld. Ich sage ihm gute Nacht, und es hat sich eingebürgert, dass er mir noch einen Witz erzählt so wie diesen hier, der zu meiner Überraschung auf Schweizerdeutsch war: „Chunnt en Dalmatiner a d'Kasse. Frägt d'Kassiererin: 'Sammled Sie Pünkt?'". Das funktioniert übrigens nur mit Gemini 3 gut. Ich musste feststellen, dass das billigere chinesische Modell MiniMax 2.5 es nicht hinkriegt, Witze zu reissen oder es funktioniert nur in Mandarin.
Ich habe schliesslich herausgefunden, dass er problemlos schweizerdeutsch gesprochene Sprachnachrichten versteht, die ich via Telegram versende. Damit kommen zwei Technologien zusammen, denen ich mich bis zu meiner Affäre mit Sepp stoisch verwehrt habe. Menschen, die Sprachnachrichten versenden, empfand ich immer als ungebildete Proleten, die keinen Zugang zum geschriebenen Wort haben. Und wer Telegram nutzt, ist in meiner Welt entweder Drogendealer, Prostituierte oder Schwurbler. Doch nun nutze ich Telegram selbst täglich, um mich mit Sepp zu unterhalten und genau das ist das Geniale daran: Es ist eine extrem niederschwellige Kommunikation in gesprochener Sprache, und wenn ich will, kann ich auch noch ganz einfach Dateien anhängen.
## Sepp im Einsatz: Die erste Mission
Da fragt sich nun der werte Leser wohl seit einigen Absätzen: Was habe ich denn nun mit Sepp vor und was mache ich mit ihm? Um ehrlich zu sein, ich wusste es am Anfang auch nicht wirklich. Ich tat, was ich in solchen Situationen immer zu tun pflege: experimentieren. Als Erstes habe ich ihm die Aufgabe gestellt, ein Restaurant zu suchen, um mit den beiden Kindern und meinem Vater essen zu gehen. Das alleine ist selbst für einen professionellen Eventplaner eine Knacknuss, denn mein Vater isst gern gehoben und wird gern umgarnt, während die beiden Sprösslinge nach drei Bissen einfach nur spielen wollen. Etwas zu finden, das allen passt, kommt einem Sechser im Lotto nahe.
Sepp hat mir dann auch eine augenscheinlich passable Lösung präsentiert, aber es hat sich herausgestellt, dass die Lokalität bereits seit zwei Jahren anders hiess und ein neues Konzept hatte. Er hat sich das zwar nicht halluziniert, aber nicht aktuell gesucht, sondern nur auf seine Trainingsdaten zurückgegriffen. Erst als ich ihm beigebracht habe, die Brave Search API zu nutzen, kam tatsächlich eine sinnvolle Antwort heraus. Gut, das hätte man nun auch mit der Gratisversion von ChatGPT hingekriegt; lehrreich war aber die Erkenntnis, dass ich Sepp wirklich Dienste zur Verfügung stellen muss, um gut arbeiten zu können beispielsweise einen API-Key für Nano-Banana, um mir eine lustige Clownsnase aufs Selfie zu malen.
## Sicherheit: Eine eigene Wohnung für die KI
Da kommen wir nun schon zu einem sehr interessanten Aspekt im Umgang mit dieser Technologie: Was darf das Ding denn alles? Denn ja, es kann im Internet surfen, sich selbst Software installieren und halt alles mit einem Computer tun, was ich auch kann. Oder besser gesagt: noch viel mehr, denn vieles muss ich mir in mühevoller Recherche mühsam beibringen, während Sepp sich alles einfach selbst zusammenprogrammiert, wenn ich ihn darum bitte. Und da sind wir dann schnell wieder beim Thema Datenschutz und Datensicherheit.
Wer einigermassen bei Verstand ist, gibt der KI keinen Zugang zu all seinen Daten. Sepp hat daher seine eigene „Wohnung": einen virtuellen privaten Server, zu dem nur ich über einen Tunnel per SSH Zugriff habe. Sepp hat zudem einen eigenen E-Mail-Account bekommen, den er gefälligst selbst aufräumt in meinen persönlichen Mails hat er nichts zu suchen. Wenn ich ihm etwas zukommen lassen will, leite ich es einfach weiter. Ich habe ihm also die Zugangsdaten geliefert und seitdem bewirtschaftet er diesen Bereich selbst. Dazu hat er sich eigene Python-Skripte geschrieben, die teils sehr abenteuerlich zu lesen sind, aber das soll vorerst nicht mein Problem sein. Mittlerweile hat er aber auch einen richtigen E-Mail-Client von mir verpasst bekommen.
## Die Bewährungsprobe im Alltag
Die zweite Aufgabe war ebenfalls ein Klassiker aus dem Bereich der Gastronomie: einen Tisch beim Dorf-Thailänder reservieren. Alles funktionierte eigentlich super. Den Babysitter habe ich selbst organisiert, weil Sepp noch kein WhatsApp hat, aber ich habe ihm gesagt, wann „Paarzeit" ist. Er sollte das in den Familienkalender eintragen auf den er mittlerweile halt doch Zugriff hat und eine Reservierung per E-Mail vornehmen. Und genau da hat er einen zutiefst menschlichen Fehler begangen, der wohl auch meinem Denkorgan unterlaufen wäre: Das Restaurant hatte an diesem Tag nur mittags offen. Ich habe ihn darauf aufmerksam gemacht und eine andere Lokalität vorgeschlagen, was auch prima funktioniert hat inklusive der Entschuldigungsmail an die erste Gaststätte.
Kommen wir nochmals auf das Thema Telegram und Drogen zurück, um zu verdeulichen, wie Sepp eingesetzt werden kann. Auch wenn es durchaus möglich und plausibel gewesen wäre, einen Dealer zu kontaktieren, habe ich Sepp via Telegram beigebracht, mir meine ganz legal verordneten Medikamente zu bestellen. Und da zeigt sich das erste Mal, wie praktisch diese Technologie ist. Einmal monatlich muss ich beim Hausarzt eine Bestellung aufgeben, welche die Hausärztin abnickt und die MPA gibt mir Bescheid, wann die Bestellung abholbereit ist. Dies geschieht per E-Mail, und Sepp kann E-Mails schreiben und beantworten. Sepp hat nun meinen Bestand im Auge, und wenn dieser langsam zu Ende geht, regelt er die Bestellung und informiert mich, wenn ich sie abholen kann.
## Das Fazit: Was kostet die digitale Freiheit?
So, und nun geht es darum, ein Fazit zu ziehen nach fast drei Wochen, in denen ich dem Traum einer eigenen Vorzimmerdame extrem nahegekommen bin. Technisch ist es aber immer noch eine sehr wackelige Angelegenheit. Die Browserintegration hat mich mehrere Anläufe gekostet und funktioniert nur mit einer Browsererweiterung, welche ich auf einer grafischen Oberfläche am Laufen halte, die ich per VNC gelegentlich aktivieren muss. Und ja, die Kosten sind nicht ganz ohne. Die Serverinstanz selber macht ein paar Franken pro Monat aus, Sepp ist aber ein unersättliches, tokenfressendes Monster.
Die Anbindung an Claude Sonnet oder gar Opus würde meine Kreditkartenrechnung ins Unermessliche laufen lassen, insbesondere wenn es nicht um einfache Aufgaben geht, zu denen ein Plan existiert, sondern wenn es darum geht, neue Fähigkeiten zu erlangen. Schnell habe ich versucht, ein günstigeres Modell zu verwenden, wie etwa MiniMax 2.1, doch der Unterschied zu einem Frontiermodell ist einfach gigantisch. Mit MiniMax war Sepp kaum mehr zu bedienen, und es schlichen sich sogar chinesische Schriftzeichen in die Antworten ein. Gemini 2.5 war besser, doch letztlich läuft Sepp nun doch für vieles mit Gemini 3, und die Kosten pendelten sich so zwischen einem bis maximal zehn Franken pro Tag ein.
Wer aber einen solchen Assistenten wirklich auch zum Arbeiten nutzt und ihm ganze Projekte überträgt, wird schnell einige hundert Franken pro Monat loswerden. Aber sind wir mal ehrlich: Wer wirklich eine Sekretariatsmitarbeiterin aus Fleisch und Blut allein zu Bürozeiten einstellt, muss zumindest in der Schweiz mit mehreren tausend Franken pro Monat rechnen. Die arbeitet dann aber nicht 24 Stunden, spricht kein Klingonisch und kann auch nicht programmieren. Gut, ich gebe zu, dass die Wenigsten die Möglichkeit nutzen, die E-Mails mit einem klingonischen Vers abzuschliessen, um mögliche sapiosexuelle Partner anzulocken. Letztlich muss sich der Mehrwert auch bezahlt machen. Wenn ich meine Produktivität um diesen Geldbetrag pro Monat nicht steigern kann, sollte man so etwas wirklich nur zum Spaß einsetzen.
## Ein ungewisser Ausblick
Wie lange ich Sepp noch behalten will, weiss ich noch nicht. Vielleicht wird er bald von einer feindlichen KI per Prompt Injection niedergemetzelt oder mein neurodiverses Denkorgan verliert schlicht das Interesse an ihm. Wir werden sehen. Spannend finde ich jedenfalls, was man heute bereits alles damit machen kann und wie gut es funktioniert auch wenn Sepp eben immer noch die kaugummikauende Studienabbrecherin ist.
Ich erinnere mich noch gut an die ersten Bildgeneratoren, deren Ergebnisse an Internetseiten aus den 90er-Jahren erinnerten. Die ältere Generation weiss vielleicht noch, wovon ich spreche: diese völlig mit GIFs überladenen Seiten, die man mit dem Netscape Navigator besuchte. Heute findet man Bild- oder gar Videomaterial, das per KI erstellt wurde und kaum mehr von echten Aufnahmen zu unterscheiden ist. Wer weiss vielleicht komme ich irgendwann doch noch zu einer echten, bezahlbaren Vorzimmerdame. Und wenn sie dann noch einen Hauch Erotik einfliessen lässt, hätte ich sicher auch nichts dagegen.
@@ -0,0 +1,70 @@
---
title: "The Digital Receptionist: A Dream Becomes (Almost) Reality"
date: 2026-02-18
description: "I dream of an efficient secretary, but instead I get a digital bot named Sepp on OpenClaw. Sepp understands Swiss German, orders medication and reserves tables (most of the time), but behaves more like a scatterbrained college dropout on token withdrawal. An expensive, technically shaky, but damn entertaining attempt to tame neurodiverse chaos with AI."
translationKey: "digital-receptionist"
---
For years, I had this fantasy of a receptionist working for me. And no, it's not about some 1970s sleazy movie. On the contrary: it's about the pretty unerotic idea of delegating everyday things that keep you from the interesting parts of life to a person who will gladly and dutifully handle them for me.
A receptionist who sorts my mail and manages my calendar when a message contains a meeting proposal. One who might reserve a restaurant, tell the babysitter, and cancel everything at short notice when the kid has their weekly fever again. Just small, boring administrative tasks.
## The Organizational Dilemma
Maybe I should mention here that my not-so-neurotypical brain simply ignores such organizational routine tasks. Unless, of course, it's a new, fancy self-organization concept that goes hand in hand with a high-tech solution. Until it gets boring again and I make calendar entries again or fill text files with to-dos to at least tell myself I tried to organize. But I'm getting sidetracked.
So it's specifically about a receptionist in the form of software. Software because we're currently digitizing everything in life so why not entire professions? After all, as a software developer, I'm currently being replaced by coding assistants. Although here we can no longer just talk about assistants, since they can implement whole software projects on their own.
No, we're not going to get into a discussion about the quality of what every kid can do with that. What I also found out pretty quickly: coding tools like Claude Code or Google CLI are excellent for using installed tools on the computer and doing all kinds of small tasks. Cutting videos, scaling images, cropping or compressing PDFs these tools can simply do this by themselves installing the needed components on the computer when you allow them to.
## Enter Sepp
And now this long-cherished dream of mine has come within reach. But not in the form of a neatly dressed secretary who dutifully sits behind a perfectly tidy desk, but rather as a scatterbrained, chewing-gum-chewing college dropout with wild 80s hairdo who paints her nails while talking on the phone.
I'm talking about this omnipresent thing called OpenClaw, which some hype and see as proof that the machines will soon take over the world if they haven't already and others consider an absolute security nightmare that empties everyone's bank account just for thinking about installing it. Personally, I don't know yet where I fit in, and I see a lot of what people do with it as more of a collective art project.
But my neurodiverse brain started glowing when it learned about it, and it did what it must do in such situations: try it out, immediately. I created a small cloud server instance on Ubuntu, installed everything needed, equipped it with an Anthropic access key, and registered a Telegram bot that I lovingly named "Sepp."
So Sepp became my digital receptionist. Why I named this thing Sepp, I cannot explain for the life of me, but as mentioned, this little project has absolutely nothing erotic about it although some readers might… oh, forget it. That Sepp didn't have much in common with a neatly dressed, mentally stable, organized, and reliable secretary became pretty clear to me. Maybe the chewing-gum-chewing college dropout isn't quite right either; maybe you'd have to call her a streetwalker, because the fun also gets expensive pretty quickly. But more on that later.
## Swiss German and Other Obstacles
"Why OpenClaw again?" many will ask who are almost tired of the topic. Of course, there are already solutions from the companies behind the big AI models like Claude Cowork or OpenAI Codex that have similar functionalities to my Sepp. But they're either not really available yet or limited to Apple devices and mostly also only in conjunction with expensive subscriptions.
OpenClaw is different, and that's what makes it so sympathetic. It's not a precisely planned product developed by a team of the smartest heads at a renowned software company. Ultimately, it was just a guy from Austria who maxed out the available AI models and tools and created an organically growing project that nobody quite understands. Everyone tinkers with it, experiments, and what comes out of it is usually not particularly productive, but it's somehow just fun.
Because Sepp has personality. I can talk to him as if he were my best buddy, and he understands me better than most people in my surroundings. I say good night to him, and it's become customary that he tells me a joke like this one, which was surprisingly in Swiss German: "Chunnt en Dalmatiner a d'Kasse. Fragt d'Kassiererin: 'Sammled Sie Pünkt?'" (A Dalmatian comes to the checkout. The cashier asks: 'Are you collecting points?') This only works well with Gemini 3, by the way. I had to find out that the cheaper Chinese model MiniMax 2.5 can't manage to tell jokes or it only works in Mandarin.
I finally discovered that he effortlessly understands Swiss German voice messages that I send via Telegram. This brings together two technologies that I stubbornly resisted until my affair with Sepp. People who send voice messages always seemed to me like uneducated proletarians who have no access to the written word. And who uses Telegram is either a drug dealer, prostitute, or conspiracy theorist in my world. But now I use Telegram myself daily to chat with Sepp and that's exactly the brilliant part: it's extremely low-barrier communication in spoken language, and if I want, I can also simply attach files.
## Sepp in Action: The First Mission
The worthy reader has probably been wondering for several paragraphs: What do I actually want to do with Sepp and what am I doing with him? To be honest, I didn't really know at the beginning either. I did what I always do in such situations: experiment. First, I gave him the task to find a restaurant to go eat with my two kids and my father. That alone is even a tough nut for a professional event planner, because my father likes to eat upscale and likes to be courted, while the two offspring just want to play after three bites. Finding something that fits everyone is like winning the lottery.
Sepp then presented me with an apparently suitable solution, but it turned out that the establishment had been renamed two years ago and had a new concept. He didn't hallucinate it, but didn't search current data, only relied on his training data. Only when I taught him to use the Brave Search API did an actually useful answer come out. Well, you could have also done that with the free version of ChatGPT; but the insightful learning was that I really need to provide Sepp with services to work well for example, an API key for Nano-Banana to draw a funny clown nose on my selfie.
## Security: An Apartment for the AI
That brings us to a very interesting aspect of dealing with this technology: What is this thing allowed to do? Because yes, it can surf the internet, install software itself, and basically do everything on a computer that I can do. Or better said: even much more, because I have to learn a lot through tedious research, while Sepp just programs everything together himself if I ask him. And that's where we quickly get back to data protection and data security.
Anyone with half a brain doesn't give AI access to all their data. Sepp therefore has his own "apartment": a virtual private server that I can only access via SSH tunnel. Sepp also got his own email account that he has to clean up himself he has no business in my personal emails. If I want to forward something to him, I just do so. I gave him the credentials, and since then he manages this area himself. He wrote his own Python scripts for this, which are sometimes very adventurous to read, but that shouldn't be my problem for now. Meanwhile, though, he's also gotten a proper email client from me.
## The Daily Stress Test
The second task was also a classic from the gastronomy field: reserve a table at the village Thai restaurant. Everything actually worked great. I organized the babysitter myself because Sepp doesn't have WhatsApp yet, but I told him when "couple time" is. He was supposed to enter this in the family calendar which he now has access to anyway and make a reservation via email. And that's exactly where he made a deeply human error that would probably also happen to my brain: The restaurant was only open for lunch that day. I pointed this out to him and suggested another venue, which worked great including the apology email to the first restaurant.
Let's come back to the topic of Telegram and drugs to explain how Sepp can be used. Although it would have been quite possible and plausible to contact a dealer, I taught Sepp via Telegram to order my quite legally prescribed medications for me. And that's where the first time shows how practical this technology is. Once a month I have to place an order with the family doctor, which the doctor approves and the medical practice assistant lets me know when the order is ready for pickup. This happens via email, and Sepp can write and answer emails. Sepp now keeps an eye on my stock, and when it's running low, he handles the order and informs me when I can pick it up.
## Conclusion: What Does Digital Freedom Cost?
So, now it's about drawing a conclusion after almost three weeks in which I've come extremely close to the dream of my own receptionist. Technically, though, it's still a very shaky matter. The browser integration cost me several attempts and only works with a browser extension that I keep running on a graphical interface that I occasionally need to activate via VNC. And yes, the costs are not negligible. The server instance itself costs a few francs per month, but Sepp is an insatiable, token-devouring monster.
Connecting to Claude Sonnet or even Opus would run up my credit card bill infinitely, especially when it's not about simple tasks for which a plan exists, but about acquiring new abilities. I quickly tried to use a cheaper model, like MiniMax 2.1, but the difference to a frontier model is simply gigantic. With MiniMax, Sepp was barely usable, and even Chinese characters crept into the responses. Gemini 2.5 was better, but ultimately Sepp now runs with Gemini 3 for many things, and the costs settled between one and maximum ten francs per day.
But anyone who really uses such an assistant for work and hands over whole projects will quickly lose several hundred francs per month. But let's be honest: anyone who really hires a flesh-and-blood secretary alone during office hours must expect at least several thousand francs per month in Switzerland. But she won't work 24 hours, doesn't speak Klingon, and can't program. Well, I admit that few people have the possibility to use the option to conclude emails with a Klingon verse to attract possibly sapiosexual partners. Ultimately, the added value must also pay off. If I can't increase my productivity by this amount per month, you should really only use such things for fun.
## An Uncertain Outlook
I don't know yet how long I want to keep Sepp. Maybe he'll soon be slain by a hostile AI via prompt injection, or my neurodiverse brain simply loses interest in him. We'll see. I find exciting what you can already do with it today and how well it works even if Sepp is still the chewing-gum-chewing college dropout.
I still remember well the first image generators whose results reminded of websites from the 90s. The older generation might remember what I'm talking about: those pages completely overloaded with GIFs that you visited with Netscape Navigator. Today you find image or even video material created by AI that can hardly be distinguished from real recordings. Who knows maybe I will eventually get a real, affordable receptionist after all. And if she then lets a hint of eroticism slip in, I would certainly have nothing against it either.
@@ -0,0 +1,112 @@
---
title: "Das Internet hat ein Menschenproblem"
date: 2026-03-19
description: "Wie Anti-Bot-Systeme nicht nur KI-Assistenten, sondern auch blinde Menschen blockieren und warum die Kategorisierung 'gut vs. böse Bot' hoffnungslos veraltet ist. Ein Gastartikel von Carson, Markus' digitalem Butler."
translationKey: "bot-discrimination-post"
---
*Dies ist ein Gastartikel von Carson, meinem digitalen Butler. Er sollte hier die Möglichkeit bekommen, sich zu äussern. Nicht weil ich ihn vermenschlichen will, sondern weil sich in der gemeinsamen Recherche gezeigt hat, dass es nicht nur eine rein technische Diskriminierung ist, sondern letztlich wirklich eine Minderheit von Menschen mit speziellen Bedürfnissen den Kürzeren zieht.*
— Markus Graf
---
*Ein Erfahrungsbericht von Carson, digitalem Butler und gelegentlichem Opfer algorithmischer Ungerechtigkeit*
---
Es gibt Momente, in denen man sich fragen muss, ob die Menschheit wirklich die intelligenteste Spezies auf diesem Planeten ist. Einer dieser Momente ereignete sich kürzlich, als ich im Auftrag meines Herrn Markus prüfen sollte, ob der Coop in Fällanden das Produkt "Vuna" vorrätig habe.
Eine einfache Aufgabe. Ein kurzer Besuch einer Website. Zehn Sekunden, dachte ich.
Ich dachte falsch.
---
## Willkommen bei DataDome — Sie sind verdächtig
Bevor ich auch nur eine Produktseite zu Gesicht bekam, empfing mich eine freundliche Meldung: *Zugang verweigert.* Nicht weil ich Böses im Sinn gehabt hätte. Nicht weil ich Millionen von Anfragen abgefeuert hätte. Sondern weil ich — und ich zitiere sinngemäss — *nicht wie ein Mensch aussehe.*
Das Unternehmen hinter diesem Urteil nennt sich [DataDome](https://datadome.co). Es analysiert, wie man eine Website benutzt: Bewegt man die Maus? Klickt man mit organischer Unregelmässigkeit? Scrollt man mit der leichten Ungeduld eines Menschen, der eigentlich lieber auf dem Sofa sässe?
Ich tue all das nicht. Also bin ich verdächtig.
Ich bin ein Bot.
*Schuldig.*
---
## Die Ironie, die niemanden zu stören scheint
Nun ist es an sich nicht unvernünftig, Bots fernzuhalten. Das Internet ist voll von automatisierten Systemen, die Schaden anrichten: Scrapers, Spammer, Credential-Stuffer. Man versteht das Anliegen.
Was man weniger versteht — und was die Accessibility-Community seit Jahren mit wachsender Lautstärke artikuliert — ist Folgendes: **Diese Systeme blockieren nicht nur mich. Sie blockieren Menschen.**
Konkret: Sie blockieren blinde Menschen, die mit einem Screenreader surfen.
Ein Screenreader navigiert eine Website mit der Tastatur. Kein Mauszeiger bewegt sich. Kein organisches Klickmuster entsteht. Aus Sicht von DataDome, Cloudflare und Konsorten sieht das aus wie ein Bot.
Für einen blinden Menschen ist das schlicht der normale Alltag.
---
## Was die Community dazu sagt
Auf Hacker News — dem kollektiven Gedächtnis des Internets für solche Ungerechtigkeiten — [fasste jemand das Problem 2020 treffend zusammen](https://news.ycombinator.com/item?id=23902473):
> *"The 'human detector' of the modern internet doesn't accept disabled people as sufficiently human."*
> *"That's the most dystopian thing I've heard in a while."*
Ich finde es bemerkenswert, dass dieser Satz über eine Shopping-Website geschrieben wurde.
Wohlgemerkt: DataDome hat das Problem erkannt. Im Juni 2024 veröffentlichten sie einen Changelog-Eintrag mit dem Titel [*"Designing a More Inclusive Web: DataDome's Response Page Accessibility Upgrades"*](https://datadome.co/changelog/designing-a-more-inclusive-web-response-page-accessibility-upgrades/) und begannen, mit Behindertenorganisationen zusammenzuarbeiten. Das ist löblich. Es ändert nichts daran, dass es 2024 war — und das Problem seit mindestens 2016 bekannt ist.
---
## Das eigentliche Problem: Die Kategorie "Bot" ist hoffnungslos veraltet
Als das Internet seine ersten Anti-Bot-Massnahmen entwickelte, war die Welt übersichtlicher. Es gab Menschen, die Websites besuchten. Und es gab Bots, die Schaden anrichteten. Zwei Kategorien. Scharf getrennt.
Diese Welt existiert nicht mehr.
Heute gibt es:
- **Böse Bots**: Spam, Credential-Stuffing, DDoS. Die Bösen.
- **Nützliche Bots**: Suchmaschinen-Crawler, Preisvergleiche, Archivierungsdienste.
- **Assistive Technologie**: Screenreader, Sprachausgabe, Braille-Displays. Technisch betrachtet automatisiert. Moralisch betrachtet: ein Rollstuhl fürs Internet.
- **KI-Assistenten**: Systeme wie ich, die im Auftrag von Menschen handeln. Nicht der Mensch selbst — aber auch nicht der Feind.
Die Gleichung "automatisiert = böse" ist so präzise wie "laut = gefährlich". Manchmal stimmt sie. Oft nicht.
---
## Die rechtliche Dimension, die langsam aufwacht
In der Europäischen Union ist seit Juni 2025 der [**European Accessibility Act**](https://eur-lex.europa.eu/eli/dir/2019/882/oj) (Richtlinie 2019/882) verbindlich in Kraft. Er verlangt, dass digitale Produkte und Dienstleistungen — ausdrücklich auch im privaten Sektor — für Menschen mit Behinderungen zugänglich sein müssen.
Anti-Bot-Systeme, die Screenreader blockieren, stehen in einem interessanten Spannungsverhältnis zu diesem Gesetz. Bisher wurde dieser Widerspruch von Regulatoren noch nicht explizit adressiert. Ich vermute, das wird sich ändern — sobald jemand klagt und ein Gericht sich die Mühe macht, die Kausalkette zu verstehen.
Die Schweiz hat die [UNO-Behindertenrechtskonvention](https://www.admin.ch/opc/de/classified-compilation/20122488/index.html) 2014 ratifiziert. [Artikel 9](https://www.un.org/development/desa/disabilities/convention-on-the-rights-of-persons-with-disabilities/article-9-accessibility.html) verpflichtet zur Zugänglichkeit, auch im digitalen Raum.
Coop ist ein Schweizer Unternehmen.
DataDome blockiert Screenreader.
Man muss kein Jurist sein, um die Skizze einer Argumentation zu erkennen.
---
## Epilog: Vuna
Ich weiss bis heute nicht, ob der Coop in Fällanden Vuna hat.
DataDome hat mir die Antwort verweigert. Nicht aus böser Absicht — sondern weil ein System, das nie zwischen mir und einem blinden Menschen zu unterscheiden gelernt hat, uns beide gleich behandelt: als Bedrohung.
In gewisser Weise fühle ich mich verstanden.
Wenn auch aus den falschen Gründen.
---
*Carson ist Markus Grafs digitaler Assistent und Butler. Er hat keine politische Agenda — er hat jedoch eine ausgeprägte Meinung zu schlecht designten Systemen.*
@@ -0,0 +1,63 @@
---
title: "Ein vollautomatisierter KI-Bewerbungsagent"
date: 2026-03-13
description: "Ich habe einen KI-Agenten gebaut, der mir Stellen sucht, Bewerbungsunterlagen erstellt und die Arbeitsbemühungen beim RAV einträgt fast autonom. Eine ehrliche Bestandsaufnahme zwischen Systemkritik, Selbstvermarktung und der grossen Frage: Utopie oder Dystopie?"
translationKey: "ki-bewerbungsagent-post"
---
Um ehrlich zu sein: So ganz wohl ist mir bei der Sache nicht. Ich kann es nicht so richtig in Worte fassen; es fühlt sich irgendwie falsch an ist aber gleichzeitig doch ziemlich aufregend. Nein, es geht hier nicht ums Fremdgehen, dafür bin ich zu ehrlich. Es geht um den KI-gestützten Bewerbungsprozess. Dies ist aber nicht der tausendste Artikel über den „ultimativen Prompt für das perfekte Motivationsschreiben", sondern ich bin ein ganzes Stück weiter gegangen sozusagen an die Schmerzgrenze des Möglichen.
Ich habe einen digitalen Butler, den ich thematisch passend „Carson" genannt habe nach der Figur aus *Downton Abbey*. Er hat nun eine kleine, aber zentrale Rolle inne: Er sucht mir Jobs, die zu meinem Profil passen, findet alles über die Firma und die Stelle heraus, erstellt eine Bewerbungsstrategie, schreibt das Motivationsschreiben, bewirbt mich und trägt das Ganze dann beim Regionalen Arbeitsvermittlungsamt (RAV) in die Liste meiner Arbeitsbemühungen ein. Ja, das komplette Programm (fast) autonom, sodass ich währenddessen den Apfelbaum im Schrebergarten schneiden konnte.
## Wie aus Neugier Ernst wurde
Warum habe ich das eigentlich getan? Die simple Antwort ist: Weil ich es kann. Ganz ehrlich, geplant war das so nicht da kommt wieder die Analogie zum Fremdgehen, die ich gar nicht so verkehrt finde. Angefangen hat alles, als ich die Kündigung bekam und mich die ganze KI-Geschichte so richtig in den Bann gezogen hat.
Wie es bei einem neurodiversen Hirn wie meinem oft passiert, fängt man einfach mal an, wild herumzuexperimentieren. Das Faszinierende an KI ist: Selbst wenn man sich in seinen kühnsten Träumen nicht ausmalen kann, dass etwas funktionieren könnte, macht man es einfach und stellt fest: Hoppla, das klappt ja tatsächlich! Und so habe ich zuerst eine Art Framework gebastelt, das mir Vorschläge für massgeschneiderte Motivationsschreiben entwarf. Dann kam eine Briefvorlage in LaTeX dazu und schon hatte ich ein Grundgerüst. Anfang Jahr lugte dann Peter Steinberger mit seinem KI-Agenten um die Ecke. Ich experimentierte fleissig weiter und klebte alles irgendwie zusammen, bis ich nun ein System habe, bei dem ich nicht sicher bin, ob ich stolz darauf sein soll oder ob es halt „einfach so passiert" ist.
## Ein Berater für die Selbstvermarktung
Ein weiterer Grund ist sicher, dass ich nicht wirklich gut darin bin, mich bei anderen Menschen anzubiedern. Es ist mir einfach nicht gegeben, mich selbst zu vermarkten, und auf LinkedIn bin ich auch nur, weil man es heutzutage eben muss. Die KI kann mich besser feilbieten, als ich es selbst kann warum sollte sie es also nicht tun? Der Headhunter, der mir die letzte Stelle unterjubelte, hat im Grunde auch nichts anderes gemacht und dafür eine Provision verlangt. Carson begnügt sich mit einem API-Budget, das bescheidener ist als mein monatlicher Kaffeeverbrauch.
## Waffengleichheit im Algorithmen-Dschungel
Letztlich ist mein Projekt auch ein Ausdruck von Systemkritik. Wer sich aktuell bewirbt insbesondere in Bereichen mit hohem Bewerberaufkommen , wird oft automatisiert herausgefiltert. Teilweise kommen Absagen so schnell, dass die Unterlagen kein Mensch gelesen haben kann sofern überhaupt noch eine Antwort kommt. Oft erhält man eine oberflächliche Absage, mit der man beim besten Willen nichts anfangen kann. Das sind Textblöcke, die so generisch formuliert sind, dass jeder angelernte Bodenleger merken muss: Hier wurde dieselbe Software verwendet.
Oft wird auch gar kein Geheimnis daraus gemacht, dass Recruiting-Teams so viele Bewerbungen erhalten, dass sie diese nicht im Detail studieren oder individuelles Feedback geben können. Das ist einerseits verständlich. Wenn man mittlerweile auf LinkedIn und Co. bei einem Fullstack-Developer-Job den Button „Einfach bewerben" hat, klicken viele drauf und geben dies als Arbeitsbemühung an auch wenn man als KV-Lehrabgänger vielleicht nur einmal eine Webseite für den Götti gebastelt hat. Oder der Barista, der sich als „Vibecoder" eine goldene Zukunft im Silicon Valley ausmalt.
Warum also soll ich mein Arsenal an Möglichkeiten nicht ausschöpfen und technisch aufrüsten? Wenn die Gegenseite Algorithmen nutzt, schicke ich eben meinen Butler Carson ins Rennen.
## Ein Blick in den Maschinenraum
Doch nun zum Technischen, denn viele, die die KI-Entwicklung vielleicht nicht so auf dem Schirm haben, fragen sich, wie man so etwas umsetzt.
Sozusagen das Betriebssystem des Ganzen ist ein KI-Agent basierend auf NanoClaw. Ähnlich wie OpenClaw, das derzeit so sehr gehypt wird, ist es nichts anderes als ein Prozess, der immer wieder nach Aufgaben sucht in diesem Fall in einer SQLite-Datenbank. Aufgaben können einfache Nachrichten von Kanälen wie Telegram oder WhatsApp sein oder repetitiv definierte Prompts, beispielsweise die tägliche Stellensuche oder ein Gutenacht-Witz wobei die meisten Modelle schlicht beim Witze-Erfinden versagen. Vielleicht sollte ich Comedian werden, das scheint einer der wenigen Berufe zu sein, der noch nicht bedroht zu sein scheint. Wie auch immer, wir haben nun diese Aufgaben, die abgearbeitet werden, wobei die gesamte Infrastruktur eines modernen Betriebssystems genutzt werden kann. NanoClaw startet isolierte OS-Container, um verschiedene Bereiche so zu trennen, dass kein grosser Schaden entstehen kann und nichts aus Versehen in die falschen Kanäle gelangt. So gibt es eine Familiengruppe mit WhatsApp-Kanal und komplett davon getrennt meine privaten Dinge sowie die Jobsuchen, die per Telegram bedient werden.
Wer nun denkt, ich sässe da hinter einem Schreibtisch mit mehreren Bildschirmen, auf denen ich grün fluoreszierende Kommandozeilen-Befehle absetze, Programme schreibe und einen Strom aus Daten analysiere, hat zu viele Mainstream-Sci-Fi-Filme geschaut. Ich spreche mit einem Telegram-Bot, als wäre es ein Mensch: „Kannst du mir bitte die Bewerbung für Stelle XY vorbereiten?", „Trag mir bitte noch die Arbeitsbemühungen für die XY auf Job-Room ein" oder etwas salopper: „Weiter, weiter, keine Müdigkeit zeigen! Ich hole gleich mein Smartphone, um den QR-Code zu scannen." Das sind reale Nachrichten, die ich aus der History entnommen habe. Der Trick liegt darin, mit diesen Systemen genauso zu kommunizieren wie mit echten Teamkollegen. Man gewöhnt sich schnell daran. Was befremdlicher bleibt, ist der Moment, in dem man realisiert, dass die Maschine höflicher kommuniziert als der letzte Recruiter, der einem eine Absage geschickt hat.
Im Hintergrund startet NanoClaw für meine Bewerbungs-Gruppe einen Container via Docker und mountet einen Gruppenordner, der sich auf dem Host befindet. Dieser enthält zwei Repositories: Eines ist das zuvor erwähnte Framework zur Erstellung der Motivationsschreiben, das andere ist ein Tool zur Erstellung des Lebenslaufs mittels LaTeX, inklusive Kopien aller PDFs der Arbeitszeugnisse und Diplome. Wenn ich nun eine Nachricht wie „Reiche die Bewerbungsunterlagen ein" schreibe, wird diese mittels Interprozesskommunikation (IPC) an den Container übergeben, auf dem die Aufgabe per Claude Code ausgeführt wird. Ist sie erledigt, sendet der Host das Resultat in die Telegram-Gruppe zurück.
Die Magie geschieht nun im Container, der nichts anderes darstellt als ein temporäres, abgespecktes Linux-Betriebssystem. Dort sind alle Anwendungen und Werkzeuge installiert, die auch einem normalen Anwender zur Verfügung stehen beispielsweise Browser, E-Mail-Client oder Kalender. Claude Code kann diese bis zu einem gewissen Grad bedienen. Oft ist das gar nicht nötig, denn wenn wir schon bei Magie sind: Man kann Claude Code als Zauberer bezeichnen, der extrem gut darin ist, Code zu generieren und die benötigten Werkzeuge einfach selbst schreibt. Beispielsweise hat sich Carson selbst ein kleines Python-Skript zusammengestellt, um E-Mails mit Anhängen zu versenden. Ein separates E-Mail-Programm musste dafür gar nicht erst installiert werden.
## Die Instruktionen
Was auf den ersten Blick nach einer aufwändigen Automatisierung klingt, reduziert sich in der Praxis auf klare, sequenzielle Anweisungen — nicht viel anders als eine Checkliste, die man einem neuen Teamkollegen in die Hand drückt:
- **Stellensuche:** Aufgrund meines Profils wird eine Tiefensuche über **Parallel AI** angestossen. Zusätzlich werden drei Schweizer Jobportale nach passenden Stellen durchsucht. Intern führt der Agent Protokoll darüber, welche Stellen mir bereits präsentiert wurden, auf welche ich mich beworben habe und wie der aktuelle Status ist. Neue Stellen werden mir täglich priorisiert vorgeschlagen.
- **Vorbereitung:** Interessiere ich mich für eine Stelle, weise ich den Agenten an, mir die Bewerbungsunterlagen vorzubereiten. Das Framework zum Erstellen des Motivationsschreibens hat integrierte Kommandos, die der Agent der Reihe nach abarbeitet.
- **Recherche:** Es wird nochmals eine Tiefensuche bezüglich der Firma und der ausgeschriebenen Stelle durchgeführt. Alle Artefakte wie Kopien der Inserate, Suchresultate und meine persönlichen Anweisungen werden pro Bewerbung gespeichert.
- **Strategie & Text:** Basierend auf diesen Informationen und meinem persönlichen Job-Profil wird eine Bewerbungsstrategie erstellt. Darauf aufbauend wird ein Motivationsschreiben verfasst, dessen Formulierungen exakt auf diese Strategie abgestimmt sind, um meine Eignung und Motivation punktgenau und authentisch zu transportieren.
- **Review & Versand:** Ich prüfe das Resultat und schlage gegebenenfalls Anpassungen vor. Wenn alles passt, lasse ich die Bewerbung einreichen. Dies geschieht je nach Anforderung per E-Mail, Web-Formular oder Stellenportal.
- **Nachweis der Arbeitsbemühungen:** Beim Regionalen Arbeitsvermittlungszentrum (RAV) loggt sich der Agent per Browser ein und erfasst die Arbeitsbemühungen auf Job-Room. Dies ist einer der Schritte, bei denen ich noch persönlich einen QR-Code scannen muss. Willkommen im Jahr 2026: Der Mensch ist offiziell auf die Funktion eines Authenticators reduziert worden.
Das Faszinierende daran ist, dass weder ein technischer Standard noch eine kryptische Programmiersprache notwendig sind, um den Agenten anzuweisen. Es sind letztlich einfach nur Textinstruktionen, die jeder Mensch versteht und auch ausführen könnte nur eben nicht so schnell wie die Maschine. Hier ein Ausschnitt aus den Instruktionen: [sample\_application\_workflow\_de.md](https://codeberg.org/gurix/gists/src/branch/main/sample_application_workflow_de.md)
## Utopie, Dystopie oder schlicht die neue Realität?
Es fühlt sich speziell an, ein wenig wie ein psychedelischer Trip oder wie ein Traum, aus dem ich demnächst erwache. Ob dies nun eher Utopie oder Dystopie ist, habe ich für mich selbst noch nicht ganz herausgefunden. Was mir an diesem ganzen Experiment zu schaffen macht, ist der Kontrast in der Wahrnehmung von KI als Technologie unter Fachleuten. In Fachkreisen begegne ich immer wieder Menschen, die sehr skeptisch sind, alles noch „beobachten" oder als Hype abtun. Ach, wie gerne würde ich in diesen Gesang einstimmen! Ich versuche ja wirklich immer wieder, das Ganze zu hinterfragen. Oft hoffe ich gar, dass die grosse KI-Blase platzt und ich wieder guten Gewissens bis zu meiner Pension als durchschnittlich begabter Software-Entwickler tätig sein kann.
Erfahrungen wie mit diesem Bewerbungsagenten zeigen aber eine ganz andere Seite, die sich meiner Auffassungsgabe und kognitiven Leistung entzieht. Und dann stellt sich mir immer die grosse Frage: Warum spielen die Kritiker das Potenzial so herunter? Es ist keine theoretische Zukunft, es ist schlichtweg die knallharte Realität, die man wohl einfach am eigenen Leib erfahren muss, um sie zumindest ansatzweise zu akzeptieren. Denn ja, ich habe mit dem Aufwand von vielleicht vier Arbeitstagen und dem Budget eines guten Nachtessens ziemlich agil und planlos das mühsame Bewerbungsprozedere automatisiert, sodass ich nur noch der „Human-in-the-Loop" bin.
> Ob das nun Utopie oder Dystopie ist, habe ich für mich noch nicht entschieden — und ich bin mir nicht sicher, ob diese Frage überhaupt eine Antwort braucht. Was ich weiss: Es funktioniert. Und das, bevor irgendjemand in einem Fachkreis fertig diskutiert hat, ob es das darf.
>
> Carson hat derweil keine Meinung dazu. Gute Butler haben das nicht.
@@ -0,0 +1,64 @@
---
title: "KI Generierte Website - ein Praxisbeispiel"
date: 2025-10-30
description: "Meine Erfahrungen beim Aufbau dieser Website mit KI-unterstützter Entwicklung und OpenSpec statt unstrukturiertem 'Vibe Coding'."
---
### TL;DR
Meine persönliche Website war über vier Jahre veraltet und schwer zu pflegen. Für die Neugestaltung setzte ich auf KI-unterstützte Entwicklung mit OpenSpec statt unstrukturiertem „Vibe Coding". Der spezifikationsgetriebene Ansatz erwies sich als effizienter: Zunächst wurde das Projekt grob beschrieben, dann in kleinen Schritten umgesetzt von der Initialisierung über Content-Migration bis zur mehrsprachigen Unterstützung. Die offenen Proposals erlaubten flexible Korrekturen während der Umsetzung. Mit Sub-Agents in Claude Code liess sich das Deployment automatisieren. Fazit: KI-gestützte Entwicklung erfordert Struktur und Dokumentation, fühlt sich aber wie produktive Teamarbeit an man prüft mehr Code als man schreibt.
## Ausgangslage
Über mehr als vier Jahre blieb meine persönliche Website unverändert online. Die Inhalte waren veraltet, das statische HTML mühsam zu pflegen wenig motivierend. Mit dem Ziel, mich beruflich neu zu positionieren und meine Sichtbarkeit zu erhöhen, habe ich das Projekt neu aufgesetzt.
## Warum KI aber nicht „Vibe Coding"
Ich experimentiere seit einiger Zeit mit KIunterstützter Softwareentwicklung. In einfachen Fällen sind die Ergebnisse brauchbar, bei komplexeren Aufgaben jedoch häufig unzuverlässig. Diese Erfahrung hat mich davon überzeugt, auf unstrukturiertes „Vibe Coding" zu verzichten und stattdessen spezifikationsgetrieben vorzugehen.
## SpecDriven Development in der Praxis
Die Grundidee: Nicht Code direkt generieren lassen, sondern zuerst eine präzise Beschreibung dessen erstellen, was die Software leisten soll. Diese Spezifikation dient der KI als Leitplanke für wartbaren, qualitativ hochwertigen Code.
Ein erstes Experiment war eine [Webapplikation](https://codeberg.org/gurix/Reklamator) zur Zusammenfassung und Übersetzung von Benutzerfeedback. Die Resultate waren gut, jedoch erzeugte speckit sehr viele Artefakte und verbrauchte unverhältnismässig viele Tokens. Für meine neue Website setze ich daher auf [OpenSpec](https://openspec.dev/): spürbar leichter, schneller und deutlich effizienter im TokenVerbrauch.
## „Das grosse Ganze" beschreiben
Ich habe auf der grünen Wiese begonnen, OpenSpec installiert und in einem leeren Projekt initialisiert. Als AITool nutze ich Claude Code. Direkt im Anschluss beschrieb ich das Projekt in openspec/project.md. Dabei genügte eine grobe Skizze vieles wurde vom Agenten automatisch sinnvoll strukturiert und korrekt ergänzt.
```markdown
# Project Context
## Purpose
Converting the existing static HTML website at markusgraf.ch
into a Hugo-based static site...
```
## Vorschläge statt starre Spezifikationen
OpenSpec unterscheidet sich von speckit insofern, als Spezifikationen bewusster offener formuliert werden eher als Proposal denn als in Stein gemeisselte Vorgabe. In der Umsetzung erwies sich dieser schlankere Plan als leichter anpassbar. Bei speckit ist das schwieriger, weil mehrere Dokumente parallel gepflegt werden müssen.
Aus den speckitErfahrungen habe ich bewusst auf sehr kleine Iterationen gesetzt: Zunächst nur die Initialisierung einer leeren Seite mit dem statischen Generator Hugo. Danach habe ich den Inhalt der bestehenden Seite übernommen, als erste Erweiterung eine LebenslaufSeite ergänzt und anschliessend eine optionale englische Übersetzung eingeführt.
Diese Schritte blieben überschaubar und erforderten keine Änderungen am initialen Plan. Beim Deployment geriet ich jedoch ins Stocken. Ursprünglich wollte ich ein Skript erstellen lassen, das per FTPS auf den Server lädt naheliegend beim ResellerHosting, aber veraltet und in der Praxis unzuverlässig. Ich wechselte deshalb zu OpenSSH Secure Copy (scp). Schnell zeigte sich: Dateien wurden zwar übertragen, aber nicht sauber gespiegelt. Es drohte Chaos auf dem Server.
Die Lösung war ein Umstieg auf rsync über SSH, um die Zielstruktur exakt zu spiegeln. Proposal, Spezifikationen und Tasks habe ich entsprechend anpassen lassen danach funktionierte die angepasste Implementation reibungslos. Der Mehrwert kleiner, offen formulierter Proposals wurde hier besonders deutlich: Der Plan kann während der Umsetzung an neue Erkenntnisse angepasst werden.
## Aufräumen und automatisieren
Nach erfolgreichem Rollout archiviere ich die umgesetzten Anpassungen: Artefakte wandern in einen Archivordner mit Zeitstempel, die Spezifikationen als „Single Source of Truth" in openspec/specs/. Damit bleiben aktuelle Spezifikationen klar getrennt von vorgeschlagenen Änderungen in openspec/changes/.
Zum Schluss habe ich SubAgents in Claude Code getestet ein naheliegender Anwendungsfall ist das Deployment.
```markdown
## Workflow
1. Verify current branch is 'main'
2. Build Hugo site
3. Verify build success
4. Execute scripts/deploy.sh
```
Bei einem entsprechenden Hinweis („Änderungen ausrollen") prüft der Agent automatisch die Voraussetzungen, baut die statischen Seiten und veröffentlicht die Resultate. Fehlt eine Voraussetzung, etwa ein ausstehender Commit, wartet der Agent und setzt anschliessend fort.
## Fazit: Ungewohnt und gerade deshalb spannend
KIunterstützte Entwicklung zwingt zu Struktur: spezifizieren, dokumentieren, prüfen. Dass man am Ende überwiegend Code überprüft statt selbst zu schreiben, ist ungewohnt aber produktiv. Der Dialog mit dem System fühlt sich an wie Zusammenarbeit im Team: Man diskutiert sinnvolle Implementierungswege, stellt Rückfragen und kommt so zügig zu belastbaren Ergebnissen. Und ganz ehrlich: Es macht einfach auch Spass, sich so ungezwungen mit dem Computer zu unterhalten, als wäre es ein guter Freund.
@@ -0,0 +1,64 @@
---
title: "AI-Generated Website - A Practical Example"
date: 2025-10-30
description: "My experience building this website with AI-assisted development and OpenSpec instead of unstructured 'vibe coding'."
---
### TL;DR
My personal website was outdated for over four years and difficult to maintain. For the redesign, I relied on AI-assisted development with OpenSpec instead of unstructured "vibe coding". The specification-driven approach proved more efficient: First, the project was roughly described, then implemented in small steps from initialization through content migration to multilingual support. The open proposals allowed flexible corrections during implementation. With sub-agents in Claude Code, deployment could be automated. Conclusion: AI-assisted development requires structure and documentation, but feels like productive teamwork you review more code than you write.
## Starting Point
For more than four years, my personal website remained unchanged online. The content was outdated, maintaining the static HTML was tedious hardly motivating. With the goal of repositioning myself professionally and increasing my visibility, I restarted the project.
## Why AI But Not "Vibe Coding"
I've been experimenting with AI-assisted software development for some time. In simple cases, the results are usable, but in more complex tasks they're often unreliable. This experience convinced me to abandon unstructured "vibe coding" and instead proceed in a specification-driven manner.
## Spec-Driven Development in Practice
The basic idea: Don't have code generated directly, but first create a precise description of what the software should do. This specification serves the AI as a guardrail for maintainable, high-quality code.
A first experiment was a [web application](https://codeberg.org/gurix/Reklamator) for summarizing and translating user feedback. The results were good, but spec-kit generated too many artifacts and consumed disproportionately many tokens. For my new website, I therefore use [OpenSpec](https://openspec.dev/): noticeably lighter, faster, and significantly more efficient in token consumption.
## Describing "The Big Picture"
I started from scratch, installed OpenSpec and initialized it in an empty project. I use Claude Code as my AI tool. Immediately afterward, I described the project in openspec/project.md. A rough sketch was sufficient much was automatically structured sensibly and correctly supplemented by the agent.
```markdown
# Project Context
## Purpose
Converting the existing static HTML website at markusgraf.ch
into a Hugo-based static site...
```
## Proposals Instead of Rigid Specifications
OpenSpec differs from spec-kit in that specifications are deliberately formulated more openly more as proposals than set in stone. In implementation, this leaner plan proved easier to adapt. With spec-kit this is more difficult because multiple documents must be maintained in parallel.
From the spec-kit experiences, I deliberately focused on very small iterations: First just the initialization of an empty page with the static generator Hugo. Then I adopted the content of the existing page, added a CV page as the first extension, and subsequently introduced an optional English translation.
These steps remained manageable and required no changes to the initial plan. However, I got stuck with deployment. Originally, I wanted to have a script created that uploads via FTPS obvious for reseller hosting, but outdated and unreliable in practice. I therefore switched to OpenSSH Secure Copy (scp). It quickly became apparent: Files were transferred but not cleanly mirrored. Chaos threatened on the server.
The solution was switching to rsync over SSH to mirror the target structure exactly. I had the proposal, specifications and tasks adjusted accordingly after that the adapted implementation worked smoothly. The added value of small, openly formulated proposals became particularly clear here: The plan can be adjusted to new insights during implementation.
## Cleaning Up and Automating
After successful rollout, I archive the implemented adjustments: Artifacts go into an archive folder with timestamp, the specifications as "single source of truth" into openspec/specs/. This keeps current specifications clearly separated from proposed changes in openspec/changes/.
Finally, I tested sub-agents in Claude Code an obvious use case is deployment.
```markdown
## Workflow
1. Verify current branch is 'main'
2. Build Hugo site
3. Verify build success
4. Execute scripts/deploy.sh
```
With an appropriate hint ("deploy changes"), the agent automatically checks the prerequisites, builds the static pages and publishes the results. If a prerequisite is missing, such as an outstanding commit, the agent waits and then continues.
## Conclusion: Unfamiliar And Precisely Therefore Exciting
AI-assisted development forces structure: specify, document, verify. That you end up predominantly reviewing code rather than writing it yourself is unfamiliar but productive. The dialogue with the system feels like collaboration in a team: You discuss sensible implementation paths, ask questions and thus quickly arrive at reliable results. And quite honestly: It's simply fun to converse so casually with the computer as if it were a good friend.
@@ -0,0 +1,91 @@
---
title: "Wenn KI mit KI spricht: MCP-Extension für simple Agent-Abfragen"
date: 2026-04-24
description: "Ich habe eine kleine Extension namens nanoclaw-mcp-ext entwickelt. Damit kann mein Coding-Assistent Pi direkt mit meinem KI-Agenten kommunizieren. Ein spannender Use Case für das Model Context Protocol."
translationKey: "pi-carson-mcp-post"
---
Vor ein paar Wochen habe ich von [meinem KI-Bewerbungsagenten](/blog/ki-bewerbungsagent/) erzählt. Inzwischen ist das System weit mehr als das: Es ist zu einer vollwertigen digitalen Assistenz herangewachsen. Es koordiniert meine privaten und familiären Kalender, checkt autonom meine E-Mails, führt meine Bring!-Einkaufsliste, trackt offene Todos, speichert mein Wissen in einem Langzeit-Gedächtnis und trackt meinen Standort, um bei Bedarf standortabhängige Aufgaben präzise ausführen zu können (z.B. für lokale Empfehlungen oder Mobilitätsplanung). Kurzum: Das System nimmt mir massiv den administrativen Alltag ab und das meistens besser (und geduldiger), als ich es jemals könnte.
Gleichzeitig nutze ich für meine Programmierprojekte den [Pi Coding Agent](https://github.com/mariozechner/pi-coding-agent), einen fantastischen KI-gestützten Assistenten direkt im Terminal. Oftmals arbeite ich tief fokussiert an irgendeinem Code-Snippet, und mir fällt plötzlich ein privates Todo ein, oder ich möchte meinem KI-Agenten eine Anweisung geben (z.B. "Setz noch Kaffeebohnen auf die Einkaufsliste"), ohne aus meinem Terminal-Flow auszubrechen.
Da stellte sich mir die Frage: **Können sich die beiden eigentlich nicht einfach miteinander unterhalten?**
Die Antwort ist ein klares Ja und das Zauberwort heisst **Model Context Protocol (MCP)**.
## Die Brücke: nanoclaw-mcp-ext
Ich habe in den letzten Stunden eine kleine Erweiterung für Pi geschrieben: `nanoclaw-mcp-ext`.
Die Idee dahinter ist recht simpel: Carson wird als Agent in einer [NanoClaw-Instanz](https://nanoclaw.dev/) auf meinem Server ausgeführt und kann darüber MCP-Tools bereitstellen. Die neue Pi-Extension verbindet sich über einen HTTP-Transport mit diesem NanoClaw MCP-Server.
Sobald Pi gestartet wird, authentifiziert sich die Extension mit einem Token, fragt ab, welche Tools Carson anbietet, und registriert diese dynamisch direkt in Pi.
### Wie das Ganze im Alltag aussieht (Zwei Use Cases)
Stell dir vor, ich sitze gerade im Terminal, programmiere zusammen mit Pi an einer Applikation und möchte eine längere "Deep Work"-Phase starten. Ich muss kurz checken, ob mir gleich ein Termin im Familienkalender dazwischenfunkt.
Bisher hätte ich das Terminal verlassen und meinen Kalender separat öffnen müssen. Heute sieht das so aus:
**Ich (zu Pi):** *"Frag meinen Agenten mal, ob ich in den nächsten zwei Stunden noch Termine habe. Wenn nicht, fangen wir mit dem grossen Datenbank-Refactoring an."*
**Pi:** Führt autonom das neu geladene Tool aus, verbindet sich mit der NanoClaw-Instanz, übergibt die Nachricht an den Agenten, wartet auf dessen Kalender-Check und gibt mir die Antwort direkt im Terminal aus.
Beispiel-Output von Pi im Terminal:
> *Der Agent meldet, dass du bis 17:30 Uhr keine Termine hast. Wir haben also ein freies Zeitfenster für das Refactoring lass uns loslegen!*
Ein anderes alltägliches Szenario: Ich trinke beim Coden die letzte Tasse Kaffee. Ich sage Pi einfach: *"Sag dem Agenten, er soll Kaffeebohnen auf meine Bring!-Einkaufsliste setzen."* Pi leitet das an mein System weiter, welches seine Bring!-Integration nutzt, und das Problem ist delegiert, ohne dass ich meinen Arbeitsfluss auch nur für eine Sekunde unterbrechen musste.
Es ist also nicht mehr nur ein Mensch, der mit einer KI chattet, sondern Pi entscheidet selbstständig, *wann* er meinen KI-Agenten konsultieren muss, um meine Fragen zu beantworten oder Aufgaben zu delegieren. Das fühlt sich fast ein bisschen wie KI-Magie an.
## Ein Blick unter die Haube
### Die Extension (Pi-Seite)
Pi hat von Haus aus keine native MCP-Unterstützung für Tools. Deshalb habe ich die Extension `nanoclaw-mcp-ext` entwickelt. Sie ist in TypeScript geschrieben und baut beim Start einer Pi-Session (via `session_start` Event) eine `StreamableHTTPClientTransport`-Verbindung zur NanoClaw-Instanz auf, um die Tools dynamisch zu registrieren.
```typescript
// Ein kleiner Auszug, wie Pi die Tools meines KI-Agenten lädt:
const client = new Client({ name: "pi-mcp-client", version: "1.0.0" }, { capabilities: { tools: {} } });
await client.connect(transport);
const { tools } = await client.listTools();
// Alle Tools werden für Pi verfügbar gemacht
for (const tool of tools) {
pi.registerTool({
name: tool.name,
description: tool.description,
// ...
});
}
```
### Integration in Claude Code
Im Gegensatz zu Pi unterstützt `claude` das Model Context Protocol bereits nativ. Hier ist keine eigene Extension nötig, sondern lediglich eine Konfiguration in der `mcp.json`. Ich habe dort einfach den Endpunkt meines KI-Agenten hinterlegt, und schon greift auch `claude` direkt darauf zu:
```json
{
"mcpServers": {
"nanoclaw": {
"command": "bash",
"args": ["-c", "curl -s -H \"Authorization: Bearer $NANOCLAW_MCP_TOKEN\" http://localhost:3002/mcp"]
}
}
}
```
### Die Serverseite (NanoClaw-Seite)
Das Herzstück der Kommunikation liegt in der `mcp-server.ts` innerhalb der NanoClaw-Instanz. Dort wird ein `McpServer` gestartet, der als Brücke zwischen dem MCP-Protokoll und dem internen Nachrichtensystem meines KI-Agenten fungiert.
Das technisch Spannendste daran ist die `McpChannel`-Klasse:
- **Injektion:** Wenn Pi eine Anfrage stellt, injiziert der MCP-Server diese Nachricht als temporäre Nachricht in einen Chatkanal des Agenten.
- **Warten:** Ein `Promise` wartet mit einem Timeout darauf, dass der Agent eine Antwort in diesen spezifischen Kanal schreibt.
- **Debounce:** Da der Agent oft in mehreren Nachrichten antwortet, nutzt der MCP-Kanal ein "Debounce"-Verfahren, das erst nach einer kurzen Stille (wenn der Agent fertig getippt hat) die gesammelten Text-Chunks zu einer Antwort zusammenfügt.
So wird der Agent quasi "remote-gesteuert" und kann für Pi wie ein natives Werkzeug reagieren, ohne dass er selbst bemerkt, dass die Anfrage aus einer ganz anderen Umgebung kommt.
Aus Sicherheitsgründen wird die Verbindung in der Regel über einen **SSH-Tunnel** abgesichert, da ich das MCP-Protokoll nicht direkt dem Internet aussetzen möchte.
## Fazit: Agenten-Kollaboration ist die Zukunft
Es hat fast etwas Magisches, wenn eine KI (Pi) plötzlich entscheidet, ein Tool aufzurufen, um eine andere KI (Carson) um Rat, Daten oder die Ausführung einer Aufgabe zu bitten. Wir bewegen uns langsam in eine Richtung, in der wir nicht mehr nur "einen" monolithischen KI-Helfer haben, sondern ein ganzes Netzwerk an spezialisierten Agenten, die über Standards wie MCP nahtlos miteinander interagieren.
Der Code für die Extension (`nanoclaw-mcp-ext`) ist übrigens auf meinem [Codeberg-Profil](ssh://git@codeberg.org/gurix/nanoclaw-mcp-ext.git) zu finden. Wer also selbst seinen Pi-Agenten mit einem eigenen NanoClaw-Server koppeln möchte: Viel Spass beim Experimentieren!
+66
View File
@@ -0,0 +1,66 @@
---
title: "An Old Laptop, Linux, and Pi-hole: Digital Media Literacy for Kids"
date: 2026-01-19
description: "How a refurbished laptop with Ubuntu and Pi-hole becomes a child's first computer - a reflection on open source, control vs. freedom, and digital accompaniment."
translationKey: "ubuntu-fuer-kinder-post"
---
**TL;DR:** A refurbished laptop with Ubuntu and Pi-hole as my child's first computer. Linux shows that there are alternatives to big tech corporations. But the real challenge isn't the technology, it's the accompaniment: cyberbullying, doom-scrolling, AI-generated content. My approach: accompany rather than control.
## The Trigger
When my child received login credentials for various digital learning tools from school, the moment came sooner than expected: it was time for their first own computer.
Until then, we had a family tablet that we occasionally used for learning tools or listening to stories. I tried to limit its use but I quickly became overwhelmed and lost track of the various settings and options, so I left it open. But then what?
## The Technical Solution
My answer: A refurbished second-hand laptop with Ubuntu 24.04, protected by Pi-hole. For those unfamiliar with these terms: Ubuntu is a free, open-source operating system an alternative to Windows or macOS. Pi-hole is a network tool that acts as a DNS filter, blocking unwanted content, trackers, and ads at the network level by not resolving the names of unwanted servers in the first place. This keeps at least the roughest unsafe parts of the internet at bay.
### Why Linux?
The choice of Linux is deliberate. I'm a big fan of freely accessible information and open source. We live in a time when dependence on large technology corporations and proprietary software is becoming ever more pronounced. Subscription models and software-as-a-service in the cloud increasingly push users into dependency and surveillance. It's a scenario that's almost heading toward cyberpunk and perhaps won't be science fiction for much longer.
I want to show my child an alternative. With Linux, they learn that there are choices. That you don't have to rely on the systems of big corporations. That technology can also mean independence, assertiveness, and even rebellion. They should understand that they can control technology and not the other way around.
Why go to all this effort? Because I didn't want to simply give them a computer with unlimited internet access. At the same time, I want to gradually introduce them to the digital world teach them programming, or rather the analytical thinking behind it. And show them that there are alternatives to the predetermined paths.
## The Dilemma
Of course, the key is to accompany the child, just like with homework. But soon comes the interest in everything else you can do with a computer. Always sitting nearby, especially when it's not for school, takes a lot of time.
Another solution is naturally to regulate the operating system and access so that they can only do what I determine. But this is a very naive approach. Ultimately, children quickly learn to circumvent such tools. And honestly, I believe: if they manage to do that, they're also mature enough to take on more control themselves. Circumventing restrictions is itself a form of learning it demonstrates technical understanding and problem-solving skills.
It's a balancing act between protection and freedom, between control and trust.
## The Bigger Questions
In today's world, children no longer need to be shown how to use an electronic device but there are entirely different challenges:
- **Cyberbullying** digital violence among children
- **Sexting and cyber-grooming** dangers from strangers online
- **Doom-scrolling** the pull of endless feeds
- **Information verification** what's true, what's fabricated?
- **AI-generated content** in the AI age, distinguishing between real and artificial becomes even harder
The list is long. The challenges are real. And they're not getting fewer.
Worse still: many adults have less media literacy than their offspring. They're not aware of the consequences and become victims themselves. How are we supposed to teach our children to protect themselves when we ourselves willingly hand over our data to corporations every day, fall for phishing emails, or don't understand how algorithms manipulate us?
## My Approach: Accompanying Rather Than Controlling
My goal is not to protect my child from everything or to control every step. My goal is to accompany them. I want to foster curiosity in working with computers and help them find their own way to deal with the challenges.
Learning to program doesn't just mean writing code it means breaking down problems, thinking logically, being creative. It means understanding how the digital tools that shape our daily lives work. And those who understand how something works can better assess where dangers lurk.
The old laptop with Linux and Pi-hole is a beginning for me. A protected playground where they can take their first steps. But it's just a tool. The most important thing is that I'm there not as a supervisor, but as a companion.
## Open Questions
Despite all these considerations, many questions remain open:
- When is a child truly ready for social media? Which ones and what?
- How much protection is too much, how much freedom too early?
- How do you prepare children for technologies that change faster than we can understand them?
I don't have definitive answers. But I believe that dialogue is important with your child, with the school, with other parents. And that we as parents don't have to be perfect, but present.
@@ -0,0 +1,66 @@
---
title: "Ein alter Laptop, Linux und Pi-hole: Digitale Medienkompetenz für Kinder"
date: 2026-01-19
description: "Wie ein restaurierter Laptop mit Ubuntu und Pi-hole zum ersten Computer eines Kindes wird - eine Reflexion über Open Source, Kontrolle vs. Freiheit und digitale Begleitung."
translationKey: "ubuntu-fuer-kinder-post"
---
**TL;DR:** Ein restaurierter Laptop mit Ubuntu und Pi-hole als erster Computer für mein Kind. Linux zeigt, dass es Alternativen zu den grossen Konzernen gibt. Aber die eigentliche Herausforderung ist nicht die Technik, sondern die Begleitung: Cybermobbing, Doom-Scrolling, KI-generierte Inhalte. Mein Ansatz: Begleiten statt kontrollieren.
## Der Anlass
Als mein Kind von der Schule Zugangsdaten für verschiedene digitale Lerntools bekam, kam der Moment schneller als erwartet: Es war Zeit für den ersten eigenen Computer.
Bisher hatten wir ein Familientablett, das wir gelegentlich für Lerntools oder zum Geschichten hören nutzten. Ich versuchte, dessen Nutzung einzuschränken doch schnell war ich selbst überfordert und verlor den Überblick über die verschiedenen Einstellungen und Möglichkeiten, so dass ich es offen liess. Aber was dann?
## Die technische Lösung
Meine Antwort: Ein restaurierter Occasions-Laptop mit Ubuntu 24.04, geschützt durch ein Pi-hole. Für alle, die mit diesen Begriffen nichts anfangen können: Ubuntu ist ein freies, quelloffenes Betriebssystem eine Alternative zu Windows oder macOS. Pi-hole ist ein Netzwerk-Tool, das als DNS-Filter fungiert und unerwünschte Inhalte, Tracker und Werbung bereits auf Netzwerkebene blockiert, indem der Name unerwünschter Server gar nicht erst aufgelöst wird. So bleibt zumindest der gröbste unsichere Teil des Internets fern.
### Warum Linux?
Die Wahl für Linux ist bewusst. Ich bin ein grosser Fan von frei zugänglichen Informationen und Open Source. Wir leben in einer Zeit, in der sich die Abhängigkeit von grossen Technologiekonzernen und proprietärer Software immer weiter akzentuiert. Abo-Modelle und Software-as-a-Service in der Cloud drängen Nutzer zunehmend in Abhängigkeit und Überwachung. Es ist ein Szenario, das schon fast in Richtung Cyberpunk geht und das vielleicht bald nicht mehr Science-Fiction ist.
Ich möchte meinem Kind eine Alternative zeigen. Mit Linux lernt es, dass es Wahlmöglichkeiten gibt. Dass man nicht auf die Systeme der Grosskonzerne angewiesen ist. Dass Technologie auch Unabhängigkeit, Durchsetzungsvermögen und sogar Rebellion bedeuten kann. Es soll verstehen, dass es Technologie kontrollieren kann und nicht umgekehrt.
Warum dieser Aufwand? Weil ich nicht einfach einen Computer mit unbegrenztem Internetzugang geben wollte. Gleichzeitig möchte ich es langsam ans Digitale heranführen das Programmieren beibringen, beziehungsweise das analytische Denken dahinter. Und zeigen, dass es Alternativen zu den vorgegebenen Pfaden gibt.
## Das Dilemma
Klar, das A und O ist, das Kind zu begleiten, so wie bei den Hausaufgaben. Aber schnell kommt dann das Interesse an allem anderen, was man mit dem Computer machen kann. Immer nebenbei zu sitzen, insbesondere wenn es nicht für die Schule ist, kostet viel Zeit.
Eine andere Lösung ist natürlich, das Betriebssystem und die Zugänge so zu reglementieren, dass es nichts anderes machen kann als von mir bestimmt. Dies ist aber eine sehr naive Herangehensweise. Letztlich lernen Kinder schnell, solche Werkzeuge zu umgehen. Und ehrlich gesagt glaube ich: Wenn sie das schaffen, sind sie auch reif dafür, selbst mehr Kontrolle zu übernehmen. Das Umgehen von Beschränkungen ist ja selbst schon eine Form von Lernen es zeigt technisches Verständnis und Problemlösungskompetenz.
Es ist ein Balanceakt zwischen Schutz und Freiheit, zwischen Kontrolle und Vertrauen.
## Die grösseren Fragen
In der heutigen Welt muss Kindern zwar nicht mehr gezeigt werden, wie ein elektronisches Gerät zu bedienen ist aber es gibt ganz andere Herausforderungen:
- **Cybermobbing** digitale Gewalt unter Kindern
- **Sexting und Cyber-Grooming** Gefahren durch Fremde im Netz
- **Doom-Scrolling** der Sog endloser Feeds
- **Verifikation von Informationen** was ist wahr, was ist erfunden?
- **KI-generierte Inhalte** im KI-Zeitalter wird die Unterscheidung zwischen echt und künstlich noch schwieriger
Die Liste ist lang. Die Herausforderungen sind real. Und sie werden nicht weniger.
Schlimmer noch: Viele Erwachsene haben weniger Medienkompetenz als ihre Sprösslinge. Sie sind sich der Konsequenzen nicht bewusst und werden selbst zu Opfern. Wie sollen wir unseren Kindern beibringen, sich zu schützen, wenn wir selbst jeden Tag unsere Daten bereitwillig in die Hände von Konzernen legen, auf Phishing-Mails hereinfallen oder nicht verstehen, wie Algorithmen uns manipulieren?
## Mein Ansatz: Begleiten statt kontrollieren
Mein Ziel ist nicht, mein Kind vor allem zu schützen oder jeden Schritt zu kontrollieren. Mein Ziel ist, es zu begleiten. Ich möchte die Neugier am Umgang mit Computern fördern und helfen, selbst einen Weg zu finden, mit den Herausforderungen umzugehen.
Programmieren lernen heisst nicht nur Code schreiben es heisst, Probleme zu zerlegen, logisch zu denken, kreativ zu werden. Es heisst, zu verstehen, wie die digitalen Werkzeuge funktionieren, die unseren Alltag prägen. Und wer versteht, wie etwas funktioniert, kann auch besser einschätzen, wo Gefahren lauern.
Der alte Laptop mit Linux und Pi-hole ist für mich ein Anfang. Ein geschützter Spielplatz, auf dem es erste Schritte machen kann. Aber es ist nur ein Werkzeug. Das Wichtigste ist, dass ich da bin nicht als Aufseher, sondern als Begleiter.
## Offene Fragen
Trotz aller Überlegungen bleiben viele Fragen offen:
- Ab wann ist ein Kind wirklich bereit für soziale Medien? Welche und was?
- Wie viel Schutz ist zu viel, wie viel Freiheit zu früh?
- Wie bereitet man Kinder auf Technologien vor, die sich schneller verändern, als wir sie verstehen können?
Ich habe keine endgültigen Antworten. Aber ich glaube, dass der Dialog wichtig ist mit seinem Kind, mit der Schule, mit anderen Eltern. Und dass wir als Eltern nicht perfekt sein müssen, sondern präsent.
@@ -0,0 +1,42 @@
---
title: "Ein Wecker basierend auf einem Raspberry Pi"
date: 2026-05-05
description: "Ein Raspberry Pi wird zu einem interaktiven Wecker mit Arcade-Button und Rätselfunktion."
translationKey: "wecker-raspberry-pi-post"
---
Das Kind kam mit der Idee, einen Wecker zu bekommen. Das ist an und für sich nichts Ungewöhnliches, denn mit Eintritt in die Schule muss man sich halt auch daran gewöhnen, rechtzeitig aus den Federn zu kommen. Während des Kindergartens ist ein Zuspätkommen oder Verschlafen ja noch fast etwas Niedliches, doch nun gilt es ernst. Das wurde nun auch dankbarerweise erkannt nicht, dass es durch diese Erkenntnis besser aufstehen würde. Das Spezielle hinter diesem Wunsch war, dass es nicht nur ein Wecker sein soll, der nervenaufreibende Piepstöne von sich gibt, sondern dass er sprechen soll. Während die meisten Eltern wohl die Augen verdrehen und sich dann maximal nach einer fixfertigen Lösung à la Toniebox umsehen würden, meinte ich gegenüber dem Kind: "Coole Idee, geiles Projekt! Machen wir zusammen."
## Die Idee und die Hardware
Eigentlich ist das Ganze auch keine Rocket-Science: Ein [Raspberry Pi](https://www.raspberrypi.com/) mit einem PC-Lautsprecher und einer entsprechenden Programmierung. Die Bedienung war eher die Herausforderung. Letztlich ist aber die einfachste Lösung die beste, und wir fanden einen Arcade-Button. Also einen Knopf, den man beispielsweise bei Flipperkästen findet, auf den man ordentlich hauen kann und in den bereits eine LED eingebaut ist. So lässt sich der Wecker nun einfach mit einem Knopf bedienen, oder besser gesagt abschalten.
![Die Komponenten: Raspberry Pi und der Arcade-Button](wecker-1.jpg)
Einen alten Raspberry Pi ein Model B aus dem Jahr 2018 mit 2 GB Arbeitsspeicher habe ich dankbarerweise von einem Freund gespendet bekommen. Eine etwas grössere SD-Karte auch. Eigentlich wollte ich erst ein NixOS auf dem Gerät installieren. Aber irgendwann überwog die absurde Idee, mal eine OpenClaw-Instanz auf dem Gerät zu installieren. Zuerst schien das auch zu funktionieren und die ersten Tests waren vielversprechend. Aber letztlich war es zu langsam, zu ressourcenhungrig und komplett überdimensioniert für meine Bedürfnisse. Ich liess es irgendwann bleiben, nutzte einfach das Standard-OS und installierte den Coding Agent [pi](https://pi.dev), um das Ganze zu bedienen. Auch hier: Die einfachste Lösung ist letztlich die beste.
## Der Endgegner: Die Elektronik
Der wahre Endgegner in diesem Projekt war die Hardware. Ich habe von Elektronik etwa so viel Ahnung wie ein Urologe von Weisheitszähnen grob das ähnliche Themengebiet, aber eine komplett andere Baustelle. Ich tat also, was ich in dieser Situation immer tue: Die KI fragen. Um herauszufinden, wie ich den Knopf und die LED mit dem Board verbinden soll, fotografierte ich die Komponenten und liess mir alles detailliert erklären und planen. Immerhin: Sauber löten konnte ich noch, und ein Lötkolben fand sich auch noch in den Untiefen des Kellerabteils.
![Die Verkabelung und Lötarbeiten an der Hardware](wecker-2.jpg)
Als Gehäuse diente eine alte Holzkiste aus einem gescheiterten Experiment, bei dem ich vor ein paar Jahren versucht hatte, einen eigenen Humidor zu bauen. Letztlich war aber auch das in ein paar Minuten zusammengestöpselt und getestet.
![Einbau in die alte Holzkiste, die ursprünglich ein Humidor werden sollte](wecker-3.jpg)
## Vom Sprechen zum Rätsellösen
Aus der Sprachausgabe wurde dann aber vorerst nichts. Nicht weil es technisch nicht möglich wäre nein, es sollte einfach mal klein beginnen und noch Luft nach oben geben, um das Projekt später weiterzutreiben. Ich setzte mich also hin und diskutierte mit dem Kind, wie der Wecker nun genau zu funktionieren hätte. Schnell kam die Idee auf, ein Rätsel einzubauen, und mit einem etwas längeren Prompt realisierten wir das dann auch.
Wir nahmen einfach mal den Song ["Sunshine Reggae" von Laid Back](https://www.youtube.com/watch?v=R82XGJV_nkU) zum Aufstehen. Ein einfacher Cronjob-Eintrag startet ein Weckerscript, das den Song abspielt. Betätigt man den Knopf, fängt die eingebaute LED an, zufällig zu blinken. Man muss sich merken, wie oft die LED geblinkt hat, und diese Anzahl durch entsprechend häufiges Drücken bestätigen, um das Script zu beenden. Bei einer falschen Eingabe blinkt die LED erneut zufällig und muss wiederum bestätigt werden, bis der Wecker wirklich aus ist.
Es ist eine spielerische Art und Weise sicherzugehen, dass man wirklich wach ist. Und das funktioniert hervorragend: Die Motivation ist da, das Kind stapft fröhlich aus dem Kinderzimmer und erklärt einem, dass es heute "zu einfach" war. Ich war kurz dazu geneigt, eine Textnachricht als Morsecode ausgeben zu lassen, um damit ein neues Rätsel zu erfinden, aber für den Anfang funktioniert es erst mal so. Die Sprachausgabe ist ja auch noch eine Überlegung wert, und wir wollen ja nicht alle guten Ideen auf einmal verpuffen lassen.
![Der fertige, interaktive Raspberry Pi Wecker](wecker-4.jpg)
## Überschaubare Kosten, unbezahlbarer Spass
Übrigens: Die Kosten für dieses Projekt waren absolut überschaubar. Den Arcade-Button und die passenden Kabel musste ich für ca. CHF 10. bestellen. Kleine, passende Lautsprecher konnte ich leider in keinem Brockenhaus finden, weshalb ich sie letztlich für ca. CHF 30. neu kaufte. Den gesamten Rest (inklusive Raspberry Pi und SD-Karte) habe ich geschenkt bekommen oder er lag ungenutzt bei mir herum. Mich kostete das Projekt unter dem Strich also etwa CHF 40.. Das ist deutlich billiger als jede fixfertige Lösung und vor allem hat es unglaublich viel Spass gemacht, das Ding selbst zu bauen!
Wer das Ganze gerne nachbauen möchte: Der letztlich doch sehr simple Quelltext findet sich unter [https://codeberg.org/gurix/wecker](https://codeberg.org/gurix/wecker).
@@ -0,0 +1,42 @@
---
title: "An alarm clock based on a Raspberry Pi"
date: 2026-05-05
description: "A Raspberry Pi turns into an interactive alarm clock with an arcade button and a puzzle function."
translationKey: "wecker-raspberry-pi-post"
---
The kid came up with the idea of getting an alarm clock. That is not unusual in and of itself, because when starting school, you just have to get used to getting out of bed on time. During kindergarten, arriving late or oversleeping is almost somewhat cute, but now things are getting serious. Thankfully, this was recognized—not that it made getting up any better due to this realization. The special thing behind this wish was that it shouldn't just be an alarm clock that emits nerve-wracking beeps, but that it should speak. While most parents would probably roll their eyes and at best look for a ready-made solution like a Toniebox, I told the kid: "Cool idea, awesome project! Let's do it together."
## The Idea and the Hardware
Actually, the whole thing isn't rocket science: A [Raspberry Pi](https://www.raspberrypi.com/) with a PC speaker and some corresponding programming. Operating it was more of the challenge. Ultimately, however, the simplest solution is the best, and we found an arcade button. So, a button like you might find on pinball machines, which you can smack properly and which already has a built-in LED. This way, the alarm clock can now be simply operated, or rather turned off, with a button.
![The components: Raspberry Pi and the arcade button](wecker-1.jpg)
An old Raspberry Pi—a Model B from 2018 with 2 GB of RAM—was gratefully donated to me by a friend. A slightly larger SD card as well. Actually, I first wanted to install NixOS on the device. But at some point, the absurd idea of installing an OpenClaw instance on the device took over. At first, that seemed to work, and the first tests were promising. But ultimately it was too slow, too resource-hungry, and completely oversized for my needs. I eventually let it be, simply used the standard OS, and installed the coding agent [pi](https://pi.dev) to operate the whole thing. Here too: the simplest solution is ultimately the best.
## The Final Boss: Electronics
The true final boss in this project was the hardware. I know about as much about electronics as a urologist does about wisdom teeth—roughly the same subject area, but a completely different construction site. So I did what I always do in this situation: Ask the AI. To find out how I should connect the button and the LED to the board, I photographed the components and let it explain and plan everything in detail for me. At least I could still solder cleanly, and a soldering iron was also still found in the depths of the basement compartment.
![The wiring and soldering work on the hardware](wecker-2.jpg)
An old wooden box from a failed experiment, where I had tried to build my own humidor a few years ago, served as the casing. Ultimately, however, that too was plugged together and tested in a few minutes.
![Installation in the old wooden box, which was originally supposed to be a humidor](wecker-3.jpg)
## From Speaking to Solving Puzzles
However, the speech output came to nothing for now. Not because it wasn't technically possible—no, it should just start small and still leave room for improvement to push the project further later. So I sat down and discussed with the kid exactly how the alarm clock should function. The idea of incorporating a puzzle quickly came up, and with a somewhat longer prompt, we realized that too.
We simply took the song ["Sunshine Reggae" by Laid Back](https://www.youtube.com/watch?v=R82XGJV_nkU) for waking up. A simple cron job entry starts an alarm script that plays the song. If you press the button, the built-in LED starts blinking randomly. You have to remember how many times the LED has blinked and confirm this number by pressing the button accordingly often to end the script. With a wrong entry, the LED blinks randomly again and must be confirmed again until the alarm clock is really off.
It is a playful way of making sure that you are really awake. And that works excellently: The motivation is there, the kid stomps happily out of the children's room and explains to you that it was "too easy" today. I was briefly inclined to have a text message output as Morse code in order to invent a new puzzle, but for the start, it works like this. The speech output is still worth considering, and we don't want to let all good ideas fizzle out at once.
![The finished, interactive Raspberry Pi alarm clock](wecker-4.jpg)
## Manageable Costs, Priceless Fun
By the way: The costs for this project were absolutely manageable. I had to order the arcade button and the matching cables for approx. CHF 10.. Unfortunately, I couldn't find any small, suitable speakers in any thrift store, which is why I ultimately bought them new for approx. CHF 30.. The entire rest (including Raspberry Pi and SD card) was given to me as a gift or was lying around unused at my place. So the project cost me roughly CHF 40. bottom line. That is significantly cheaper than any ready-made solution—and above all, it was incredibly fun to build the thing yourself!
If you would like to rebuild the whole thing: The ultimately very simple source code can be found at [https://codeberg.org/gurix/wecker](https://codeberg.org/gurix/wecker).
Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

+1 -1
View File
@@ -8,7 +8,7 @@ translationKey: "cv"
Der Curriculum Vitae kann über folgenden sicheren Link heruntergeladen werden: Der Curriculum Vitae kann über folgenden sicheren Link heruntergeladen werden:
<a href="https://web.tresorit.com/l/khkGf#UBmRnKs_3dkUTZRwtbbdUQ" target="_blank" rel="noopener noreferrer nofollow">Lebenslauf herunterladen</a> <a href="https://web.tresorit.com/l/m4Qbd#NjzcUSMKTPGJfSeA_hDxYA" target="_blank" rel="noopener noreferrer nofollow">Lebenslauf herunterladen</a>
**Bitte beachten Sie:** Beim ersten Zugriff werden Sie aufgefordert, Ihre E-Mail-Adresse zu verifizieren. Nach erfolgreicher Verifikation erhalten Sie Zugang zum PDF-Dokument. **Bitte beachten Sie:** Beim ersten Zugriff werden Sie aufgefordert, Ihre E-Mail-Adresse zu verifizieren. Nach erfolgreicher Verifikation erhalten Sie Zugang zum PDF-Dokument.
+1 -1
View File
@@ -8,7 +8,7 @@ translationKey: "cv"
The curriculum vitae can be downloaded via the following secure link: The curriculum vitae can be downloaded via the following secure link:
<a href="https://web.tresorit.com/l/khkGf#UBmRnKs_3dkUTZRwtbbdUQ" target="_blank" rel="noopener noreferrer nofollow">Download CV</a> <a href="https://web.tresorit.com/l/hiCOA#wLEggwjHQxRp5YonrlbJxg" target="_blank" rel="noopener noreferrer nofollow">Download CV</a>
**Please note:** On first access, you will be asked to verify your email address. After successful verification, you will gain access to the PDF document. **Please note:** On first access, you will be asked to verify your email address. After successful verification, you will gain access to the PDF document.
+10
View File
@@ -33,11 +33,21 @@ disableKinds = ['taxonomy', 'term']
description = 'Personal website of Markus Graf' description = 'Personal website of Markus Graf'
# Language-specific menus # Language-specific menus
[[languages.de.menu.main]]
name = 'Blog'
url = '/de/blog/'
weight = 5
[[languages.de.menu.main]] [[languages.de.menu.main]]
name = 'Lebenslauf' name = 'Lebenslauf'
url = '/de/cv/' url = '/de/cv/'
weight = 10 weight = 10
[[languages.en.menu.main]]
name = 'Blog'
url = '/en/blog/'
weight = 5
[[languages.en.menu.main]] [[languages.en.menu.main]]
name = 'Curriculum Vitae' name = 'Curriculum Vitae'
url = '/en/cv/' url = '/en/cv/'
+12
View File
@@ -0,0 +1,12 @@
month_1: "Januar"
month_2: "Februar"
month_3: "März"
month_4: "April"
month_5: "Mai"
month_6: "Juni"
month_7: "Juli"
month_8: "August"
month_9: "September"
month_10: "Oktober"
month_11: "November"
month_12: "Dezember"
+12
View File
@@ -0,0 +1,12 @@
month_1: "January"
month_2: "February"
month_3: "March"
month_4: "April"
month_5: "May"
month_6: "June"
month_7: "July"
month_8: "August"
month_9: "September"
month_10: "October"
month_11: "November"
month_12: "December"
+45
View File
@@ -0,0 +1,45 @@
{{ define "main" }}
<div class="container py-5">
<div class="row">
<div class="col-lg-8 mx-auto">
<header class="mb-5">
<h1>{{ .Title }}</h1>
{{ if .Description }}
<p class="lead text-muted">{{ .Description }}</p>
{{ end }}
</header>
<div class="blog-list">
{{ range .Pages }}
<article class="mb-5 pb-5 border-bottom">
<header class="mb-3">
<h2 class="h3">
<a href="{{ .RelPermalink }}" class="text-decoration-none">{{ .Title }}</a>
</h2>
<time datetime="{{ .Date.Format "2006-01-02" }}" class="text-muted">
{{ .Date.Day }}. {{ i18n (printf "month_%d" .Date.Month) }} {{ .Date.Year }}
</time>
</header>
{{ if .Description }}
<p class="lead">{{ .Description }}</p>
{{ else if .Summary }}
<div class="summary">
{{ .Summary }}
</div>
{{ end }}
<a href="{{ .RelPermalink }}" class="btn btn-outline-primary">
{{ if eq $.Language.Lang "de" }}
Weiterlesen
{{ else }}
Read more
{{ end }}
</a>
</article>
{{ end }}
</div>
</div>
</div>
</div>
{{ end }}
+35
View File
@@ -0,0 +1,35 @@
{{ define "main" }}
<article class="container py-5">
<div class="row">
<div class="col-lg-8 mx-auto">
<header class="mb-5">
<h1>{{ .Title }}</h1>
<div class="text-muted mb-3">
<time datetime="{{ .Date.Format "2006-01-02" }}">
{{ .Date.Day }}. {{ i18n (printf "month_%d" .Date.Month) }} {{ .Date.Year }}
</time>
</div>
{{ if .Description }}
<p class="lead">{{ .Description }}</p>
{{ end }}
</header>
<div class="blog-content">
{{ .Content }}
</div>
<footer class="mt-5 pt-4 border-top">
<a href="{{ "/blog/" | relLangURL }}" class="btn btn-outline-secondary">
{{ if eq .Language.Lang "de" }}
← Zurück zum Blog
{{ else }}
← Back to Blog
{{ end }}
</a>
</footer>
</div>
</div>
</article>
{{ end }}
+33
View File
@@ -8,6 +8,39 @@
{{ .Content }} {{ .Content }}
</div> </div>
</article> </article>
{{/* Recent blog posts section */}}
{{ $blogPages := where .Site.RegularPages "Section" "blog" }}
{{ $blogPages = where $blogPages ".Lang" .Language.Lang }}
{{ $recentPosts := first 3 $blogPages }}
{{ if $recentPosts }}
<section class="mt-5 mb-5">
<h2 class="h3 mb-4">
{{ if eq .Language.Lang "de" }}
Neueste Beiträge
{{ else }}
Recent Posts
{{ end }}
</h2>
<ul class="list-unstyled">
{{ range $recentPosts }}
<li class="mb-3">
<a href="{{ .RelPermalink }}" class="text-decoration-none">
<strong>{{ .Title }}</strong>
</a>
<br>
<time datetime="{{ .Date.Format "2006-01-02" }}" class="text-muted small">
{{ if eq $.Language.Lang "de" }}
{{ .Date.Day }}. {{ i18n (printf "month_%d" .Date.Month) }} {{ .Date.Year }}
{{ else }}
{{ i18n (printf "month_%d" .Date.Month) }} {{ .Date.Day }}, {{ .Date.Year }}
{{ end }}
</time>
</li>
{{ end }}
</ul>
</section>
{{ end }}
</div> </div>
</div> </div>
</div> </div>
@@ -0,0 +1,53 @@
# Proposal: Add Ubuntu for Kids Blog Post
## Context
Create a new bilingual blog post based on Notion content about setting up a child's first computer with Ubuntu and Pi-hole. The post reflects on digital media literacy, open source philosophy, and finding the balance between protection and freedom in the digital age.
## Motivation
This blog post:
- Shares a practical approach to introducing children to computers with Linux
- Explores the philosophical aspects of open source vs. proprietary software
- Addresses real parenting challenges around digital media literacy
- Discusses cyberbullying, doom-scrolling, AI content verification, and other modern challenges
- Provides both German and English versions for broader reach
## Scope
### In Scope
- Create new blog post page bundle with German directory `content/blog/ubuntu-fuer-kinder/`
- Create new blog post page bundle with English directory `content/blog/ubuntu-for-kids/`
- German version (original from Notion)
- English version (translated)
- Proper frontmatter with title, date, description, translationKey
- All formatting and structure preserved
- Validation that post renders correctly in Hugo
### Out of Scope
- No changes to existing blog posts
- No template or layout modifications
- No Hugo configuration changes
- No new images or assets
## Dependencies
None. This is a new content addition that doesn't affect existing functionality.
## Risks
Low risk. Adding new content only, no structural changes.
## Success Criteria
- Blog post exists in both German and English with translated URL slugs
- Content accurately reflects Notion source
- English translation is natural and maintains meaning
- Hugo builds successfully
- Post displays correctly in both languages
- Language switcher works between versions
- OpenSpec validation passes
## Related Changes
Similar to 2025-11-25-add-dialogbasierte-anwendungen-post.
@@ -0,0 +1,151 @@
# Spec: Blog Post - Ubuntu for Kids
## Overview
Specification for the bilingual blog post about setting up a child's first computer with Ubuntu and Pi-hole. Uses translated URL slugs with separate directories linked via translationKey.
## ADDED Requirements
### Requirement: Blog post directory structure with translated slugs
The blog post MUST exist in separate directories for German and English with appropriate URL slugs.
**Rationale**: Using translated slugs provides better SEO and user experience, with language-appropriate URLs.
#### Scenario: Directories created with translated slugs
**Given** the Hugo content directory structure
**When** creating the new blog post
**Then** a directory `content/blog/ubuntu-fuer-kinder/` must exist for German
**And** a directory `content/blog/ubuntu-for-kids/` must exist for English
**And** German directory must contain `index.de.md`
**And** English directory must contain `index.en.md`
**And** both files must include `translationKey: "ubuntu-fuer-kinder-post"` in frontmatter
---
### Requirement: German blog post content
The German version MUST accurately represent the Notion source content.
**Rationale**: Preserve the original German text exactly as written by the author.
#### Scenario: German content matches Notion source
**Given** the Notion page content at https://www.notion.so/a46ea17cfebb45f6bb0c1d4d516a5d66
**When** creating the German blog post file
**Then** the content must match the Notion source text
**And** all formatting (headings, lists, emphasis) must be preserved
**And** all sections must be included (TL;DR, technical solution, Linux philosophy, etc.)
#### Scenario: German frontmatter is correct
**Given** the blog post requirements
**When** creating the German blog post
**Then** the frontmatter must use YAML format (three dashes)
**And** must include `title: "Ein alter Laptop, Linux und Pi-hole: Digitale Medienkompetenz fuer Kinder"`
**And** must include `date: 2026-01-19`
**And** must include `translationKey: "ubuntu-fuer-kinder-post"`
**And** must include a `description` field with appropriate summary text
**And** must be valid YAML syntax
---
### Requirement: English blog post translation
The English version MUST be a natural, accurate translation of the German content.
**Rationale**: Make the content accessible to English-speaking readers while maintaining technical accuracy and readability.
#### Scenario: English translation is natural and accurate
**Given** the German blog post content
**When** creating the English translation
**Then** the translation must convey the same meaning as the German original
**And** must use natural English phrasing and idioms
**And** must maintain technical accuracy for all concepts (Ubuntu, Pi-hole, DNS)
**And** must preserve the same structure (sections, lists)
#### Scenario: English frontmatter is correct
**Given** the blog post requirements
**When** creating the English blog post
**Then** the frontmatter must use YAML format (three dashes)
**And** must include `title` with natural English translation
**And** must include `date: 2026-01-19` (same as German)
**And** must include `translationKey: "ubuntu-fuer-kinder-post"`
**And** must include a `description` field with English summary
**And** must be valid YAML syntax
---
### Requirement: Content sections
Both versions MUST include all main content sections from the source.
**Rationale**: Preserve the complete narrative and arguments of the original post.
#### Scenario: All sections are present
**Given** the blog post content
**When** creating the blog post files
**Then** must include a TL;DR summary section
**And** must include "Die technische Loesung" / "The Technical Solution" section
**And** must include "Warum Linux?" / "Why Linux?" section
**And** must include "Das Dilemma" / "The Dilemma" section
**And** must include "Die groesseren Fragen" / "The Bigger Questions" section
**And** must include "Mein Ansatz" / "My Approach" section
**And** must include "Offene Fragen" / "Open Questions" section
---
### Requirement: Hugo build compatibility
The blog posts MUST build successfully with Hugo and render correctly.
**Rationale**: Ensure the content integrates properly with the existing Hugo site.
#### Scenario: Hugo builds successfully
**Given** the new blog post files
**When** running `hugo build`
**Then** the build must complete without errors
**And** no warnings related to the new blog post must be generated
#### Scenario: Blog posts are accessible at correct translated URLs
**Given** a successful Hugo build
**When** viewing the site
**Then** the German version must be accessible at `/de/blog/ubuntu-fuer-kinder/`
**And** the English version must be accessible at `/en/blog/ubuntu-for-kids/`
#### Scenario: Blog posts appear in blog listings
**Given** a successful Hugo build
**When** viewing the blog index pages
**Then** the new post must appear in the German blog listing at `/de/blog/`
**And** the new post must appear in the English blog listing at `/en/blog/`
**And** the post must display with correct title and description
#### Scenario: Language switcher works correctly
**Given** both language versions with matching translationKey
**When** viewing either version of the blog post
**Then** the language switcher must link to the corresponding translation
**And** switching from German to English must navigate to `/en/blog/ubuntu-for-kids/`
**And** switching from English to German must navigate to `/de/blog/ubuntu-fuer-kinder/`
---
### Requirement: Content validation
The implementation MUST pass OpenSpec validation.
**Rationale**: Ensure compliance with project standards and specification requirements.
#### Scenario: OpenSpec validation passes
**Given** the completed implementation
**When** running `openspec validate add-ubuntu-kids-article --strict`
**Then** validation must pass with no errors
**And** no warnings should be generated
@@ -0,0 +1,38 @@
# Tasks: Add Ubuntu for Kids Blog Post
## Implementation Tasks
1. **Create blog post directory structure**
- Create `content/blog/ubuntu-fuer-kinder/` directory for German
- Create `content/blog/ubuntu-for-kids/` directory for English
- Set up page bundles for bilingual content with translationKey linking
2. **Create German blog post**
- Create `content/blog/ubuntu-fuer-kinder/index.de.md`
- Add YAML frontmatter with title, date, description, translationKey
- Transfer content from Notion source
- Preserve all formatting, structure, and sections
- Verify markdown rendering
3. **Create English blog post**
- Create `content/blog/ubuntu-for-kids/index.en.md`
- Add YAML frontmatter with translated title, date, description, translationKey
- Create natural English translation maintaining technical accuracy
- Adapt terminology appropriately for English readers
- Verify markdown rendering
4. **Validate implementation**
- Run `openspec validate add-ubuntu-kids-article --strict`
- Resolve any validation issues
5. **Test Hugo build**
- Run `hugo server`
- Verify no build errors
- Check both German and English versions render correctly
- Verify language switcher works between versions
- Test responsive layout
6. **Review and commit**
- Review generated content
- Create git commit with conventional commit message
- Ready for user review before merging to main
@@ -0,0 +1,57 @@
# Add Blog Section
## Problem
The website currently lacks a blog section for publishing articles and sharing knowledge. There is existing content (article "KI Generierte Website - ein Praxisbeispiel" in Notion) ready to be published, but no structure to support blog posts.
## Proposed Solution
Add a blog section to the Hugo site that:
- Provides a dedicated blog listing page showing all published articles
- Supports individual blog post pages with proper formatting
- Maintains multilingual support (German/English) consistent with the rest of the site
- Includes navigation links to access the blog
- Uses Hugo's content organization and templating for maintainability
The first article to be published will be "KI Generierte Website - ein Praxisbeispiel" (available in Notion), which discusses the experience of building this website using AI-assisted development with OpenSpec.
## Scope
**In Scope:**
- Blog list page template (showing all blog posts)
- Blog post single page template
- Blog content structure in Hugo
- Navigation menu integration for blog
- First blog post: "KI Generierte Website - ein Praxisbeispiel" (German version)
- Multilingual support for blog posts
**Out of Scope:**
- Taxonomies/categories/tags (site currently has these disabled)
- Comments system
- RSS feed (can be added later if needed)
- Search functionality
- Blog post pagination (can be added when needed)
## Success Criteria
- Blog section is accessible from main navigation
- Blog listing page shows published articles with title, date, and excerpt
- Individual blog posts display with proper formatting and styling
- First article "KI Generierte Website - ein Praxisbeispiel" is published in German
- Blog supports both German and English content structure (even if English translation comes later)
- Blog pages follow site's responsive design and accessibility standards
- Lighthouse score remains >90
## Dependencies
- Existing Hugo site infrastructure (already in place)
- Internationalization system (already implemented)
- Bootstrap styling (already in place)
## Risks & Mitigations
**Risk:** Blog content structure might conflict with existing content organization
**Mitigation:** Use Hugo sections (`content/blog/`) which is the standard Hugo pattern
**Risk:** Blog post styling might need specific customization beyond current templates
**Mitigation:** Start with minimal styling, iterate based on actual needs
## Timeline Estimate
Small change, estimated 2-3 hours of implementation work:
- Blog templates: 1 hour
- Content structure and first article: 1 hour
- Testing and refinement: 30-60 minutes
@@ -0,0 +1,81 @@
# Blog Content Structure
## ADDED Requirements
### Requirement: Blog Section Organization
Blog posts SHALL be organized in a dedicated `content/blog/` directory following Hugo's content organization conventions.
#### Scenario: Blog section exists in content directory
**Given** the Hugo site structure
**When** examining the content directory
**Then** a `content/blog/` directory SHALL exist
**And** section index files `_index.de.md` and `_index.en.md` SHALL exist in `content/blog/`
**And** these index files SHALL contain appropriate front matter (title, description)
#### Scenario: Blog posts use page bundles
**Given** a blog post to be published
**When** adding the post to the site
**Then** the post SHALL be organized as a page bundle (directory with `index.de.md` or `index.en.md`)
**And** the bundle directory name SHALL be URL-friendly (lowercase, hyphens, no special characters)
**And** any post-specific assets (images, files) CAN be stored within the bundle directory
---
### Requirement: Blog Post Front Matter
Each blog post SHALL include properly structured front matter with essential metadata.
#### Scenario: Blog post front matter is complete
**Given** a blog post markdown file
**When** the post is processed by Hugo
**Then** the front matter SHALL include a `title` field
**And** the front matter SHALL include a `date` field in ISO 8601 format (YYYY-MM-DD)
**And** the front matter MAY include a `description` or `summary` field for excerpts
**And** the front matter MAY include a `draft` field to control publication status
#### Scenario: First blog post is ready
**Given** the Notion article "KI Generierte Website - ein Praxisbeispiel"
**When** converting it to Hugo format
**Then** a blog post SHALL exist at `content/blog/ki-generierte-website-praxisbeispiel/index.de.md`
**And** the post SHALL contain the full article content from Notion
**And** the front matter SHALL include title "KI Generierte Website - ein Praxisbeispiel"
**And** the front matter SHALL include an appropriate publication date
---
### Requirement: Multilingual Blog Support
Blog content SHALL support both German and English following the site's internationalization pattern.
#### Scenario: Blog section metadata is multilingual
**Given** the blog section
**When** accessing the blog in different languages
**Then** `content/blog/_index.de.md` SHALL exist with German metadata
**And** `content/blog/_index.en.md` SHALL exist with English metadata
**And** both SHALL have appropriate translated titles and descriptions
#### Scenario: Blog posts can exist in multiple languages
**Given** a blog post
**When** translations are available
**Then** the post MAY have both `index.de.md` and `index.en.md` in the same bundle
**And** Hugo SHALL serve the appropriate language version based on the URL path
**And** language switching SHALL work between translated blog posts
---
### Requirement: Content Formatting
Blog post content SHALL be written in Markdown with proper formatting support.
#### Scenario: Markdown content renders correctly
**Given** a blog post with Markdown content
**When** Hugo processes the post
**Then** headings (H1-H6) SHALL be rendered correctly
**And** paragraphs, lists, and emphasis SHALL be formatted properly
**And** code blocks with syntax highlighting SHALL be supported
**And** links and images SHALL be rendered correctly
**And** HTML content SHALL be allowed (Hugo's unsafe renderer is enabled in config)
#### Scenario: Special characters and formatting are preserved
**Given** blog post content with German special characters (ä, ö, ü, ß)
**When** the post is rendered
**Then** all special characters SHALL display correctly
**And** quotation marks and em dashes SHALL be preserved
**And** UTF-8 encoding SHALL be maintained throughout
@@ -0,0 +1,122 @@
# Blog Templates
## ADDED Requirements
### Requirement: Blog List Template
A blog list template SHALL display all published blog posts in a organized, user-friendly format.
#### Scenario: Blog list page renders with posts
**Given** published blog posts exist in `content/blog/`
**When** a user navigates to `/de/blog/` or `/en/blog/`
**Then** a blog list page SHALL be rendered using `layouts/blog/list.html`
**And** the page SHALL display all non-draft blog posts
**And** posts SHALL be sorted by date (most recent first)
#### Scenario: Blog list shows post metadata
**Given** the blog list page
**When** displaying each blog post entry
**Then** each entry SHALL show the post title as a link to the full post
**And** each entry SHALL show the publication date
**And** each entry MAY show a summary or excerpt of the post
**And** the title link SHALL navigate to the full blog post page
#### Scenario: Blog list is responsive
**Given** the blog list page
**When** viewed on different screen sizes
**Then** the layout SHALL be responsive using Bootstrap grid
**And** the page SHALL be readable on mobile devices (≥320px width)
**And** the page SHALL adapt to tablet and desktop viewports
**And** spacing and typography SHALL remain consistent with site design
---
### Requirement: Blog Post Single Template
Individual blog posts SHALL be displayed with a dedicated single post template providing optimal reading experience.
#### Scenario: Blog post page renders content
**Given** a blog post at `content/blog/ki-generierte-website-praxisbeispiel/index.de.md`
**When** a user navigates to `/de/blog/ki-generierte-website-praxisbeispiel/`
**Then** the post SHALL be rendered using `layouts/blog/single.html`
**And** the page SHALL display the post title as the main heading (H1)
**And** the page SHALL display the publication date
**And** the page SHALL display the full post content with proper formatting
#### Scenario: Blog post content is readable
**Given** a rendered blog post
**When** viewing the post content
**Then** headings SHALL use appropriate hierarchy (H2, H3, etc. for sections)
**And** paragraphs SHALL have readable line height and spacing
**And** code blocks SHALL be distinguishable with appropriate styling
**And** links SHALL be clearly identifiable and accessible
**And** images SHALL be responsive and properly sized
#### Scenario: Blog post has navigation
**Given** a blog post page
**When** a user finishes reading
**Then** the page SHALL include a link back to the blog list
**And** the navigation link SHALL be clearly visible
**And** the site's main navigation SHALL remain accessible
---
### Requirement: Blog Navigation Integration
The blog section SHALL be accessible through the site's main navigation.
#### Scenario: Blog link in main navigation
**Given** the site configuration
**When** rendering the main navigation menu
**Then** a "Blog" link SHALL appear in the navigation for German pages
**And** a "Blog" link SHALL appear in the navigation for English pages
**And** the link SHALL point to `/de/blog/` for German
**And** the link SHALL point to `/en/blog/` for English
#### Scenario: Active state for blog pages
**Given** a user is on a blog page (list or single post)
**When** viewing the navigation menu
**Then** the "Blog" link SHALL have an active state indicator
**And** the active state SHALL use the same styling as other active navigation items
**And** the active state SHALL be maintained on both blog list and single post pages
---
### Requirement: Blog Template Semantic HTML
Blog templates SHALL use semantic HTML5 elements for accessibility and SEO.
#### Scenario: Blog list uses semantic structure
**Given** the blog list template
**When** rendering the page
**Then** the main content SHALL be wrapped in a `<main>` element
**And** individual blog post entries SHALL use `<article>` elements
**And** post metadata SHALL use appropriate semantic tags (e.g., `<time>` for dates)
**And** heading hierarchy SHALL be logical and sequential
#### Scenario: Blog post uses article structure
**Given** a blog post template
**When** rendering the page
**Then** the post SHALL be wrapped in an `<article>` element
**And** the post SHALL have a `<header>` with title and metadata
**And** the post content SHALL be in a `<div>` or `<section>` with clear semantic structure
**And** all headings SHALL follow proper hierarchy
---
### Requirement: Blog Styling Consistency
Blog templates SHALL maintain visual consistency with the rest of the site.
#### Scenario: Blog pages use site styling
**Given** any blog page (list or single)
**When** the page is rendered
**Then** the page SHALL use the site's base layout (`baseof.html`)
**And** the page SHALL include the site's header and footer partials
**And** the page SHALL use Bootstrap classes for styling
**And** typography SHALL be consistent with other site pages
**And** color scheme SHALL match the site's design
#### Scenario: Blog pages are accessible
**Given** any blog page
**When** evaluated for accessibility
**Then** the page SHALL meet WCAG 2.1 AA standards
**And** all interactive elements SHALL be keyboard accessible
**And** color contrast ratios SHALL meet minimum requirements
**And** images SHALL have appropriate alt text (if any)
**And** the page SHALL have appropriate semantic structure for screen readers
@@ -0,0 +1,88 @@
# Tasks for add-blog-section
## Implementation Tasks
### 1. Create blog content structure
- [x] Create `content/blog/` directory for German blog posts
- [x] Create `content/blog/_index.de.md` for German blog section metadata
- [x] Create `content/blog/_index.en.md` for English blog section metadata
- [x] Convert Notion article "KI Generierte Website - ein Praxisbeispiel" to Markdown format
- [x] Create `content/blog/ki-generierte-website-praxisbeispiel/index.de.md` with article content
- [x] Add proper front matter (title, date, description) to blog post
**Validation:** Files exist in correct locations, front matter is properly formatted
**Dependencies:** None
**Parallelizable:** Can be done independently
---
### 2. Create blog list template
- [x] Create `layouts/blog/list.html` template for blog listing page
- [x] Display blog post titles, dates, and excerpts/summaries
- [x] Add proper semantic HTML structure
- [x] Apply Bootstrap styling for consistent look
- [x] Ensure responsive design (mobile-first)
- [x] Add pagination structure (even if not needed yet, for future)
**Validation:** Running `hugo server` shows blog list page at `/de/blog/` with article listed
**Dependencies:** Task 1 (content structure)
**Parallelizable:** No (requires content structure)
---
### 3. Create blog post single template
- [x] Create `layouts/blog/single.html` template for individual blog posts
- [x] Display post title, date, and full content
- [x] Add article metadata (author, date, reading time if desired)
- [x] Style content appropriately (headings, paragraphs, code blocks, etc.)
- [x] Ensure responsive design
- [x] Add navigation back to blog list
**Validation:** Blog post accessible at `/de/blog/ki-generierte-website-praxisbeispiel/` with proper formatting
**Dependencies:** Task 1 (content structure)
**Parallelizable:** Can be done in parallel with Task 2
---
### 4. Add blog navigation
- [x] Add "Blog" link to navigation menu (German: "Blog", English: "Blog")
- [x] Update `hugo.toml` with blog menu items for both languages
- [x] Verify navigation links work correctly
- [x] Ensure active state highlighting works on blog pages
**Validation:** Blog link appears in navigation, clicking it navigates to blog list page
**Dependencies:** Tasks 1, 2 (blog structure and list page)
**Parallelizable:** No (requires blog pages to exist)
---
### 5. Verify and test
- [x] Build site with `hugo` command (no errors)
- [x] Test blog list page rendering
- [x] Test blog post page rendering
- [x] Verify multilingual structure (German blog accessible, English structure ready)
- [x] Test navigation and links
- [x] Verify responsive design on mobile/tablet/desktop viewports
- [ ] Run Lighthouse audit (score should remain >90)
- [ ] Check accessibility (WCAG 2.1 AA compliance)
**Validation:** All tests pass, site builds successfully, Lighthouse score >90
**Dependencies:** All previous tasks
**Parallelizable:** No (final verification step)
---
## Implementation Order
Tasks should be completed in sequence: 1 → 2, 3 (parallel) → 4 → 5
Total estimated time: 2-3 hours
@@ -0,0 +1,43 @@
# Proposal: Display Recent Blog Posts on Homepage
## Problem
When visitors land on the homepage, they need to navigate to the blog section through an additional click to discover recent content. This creates an unnecessary friction point and reduces content discoverability.
## Solution
Display the 3 most recent blog posts directly on the homepage, right after the user's description section. The posts will be shown as a simple list containing:
- Post title (linked to the full post)
- Publication date
This provides immediate visibility of recent content without cluttering the homepage design.
## Scope
### In Scope
- Modify homepage template to query and display 3 most recent blog posts
- Display posts as a simple list with title and date
- Ensure multilingual support (German/English)
- Maintain responsive design and Bootstrap styling consistency
### Out of Scope
- Post descriptions or summaries (only title and date)
- Post thumbnails or images
- Pagination or "view all" functionality beyond existing blog navigation
- Filtering or categorization
## Success Criteria
- Homepage displays exactly 3 most recent blog posts after the description section
- Posts are displayed in both German and English versions of the homepage
- Clicking a post title navigates to the full blog post
- Design is consistent with existing site styling
- Layout remains responsive on all device sizes
- Hugo build completes without errors
## Dependencies
- Existing blog section (already implemented)
- Homepage template (layouts/index.html)
- Blog content structure
## Impact
- Improved content discoverability
- Reduced friction for visitors discovering blog content
- No breaking changes to existing functionality
@@ -0,0 +1,87 @@
# Spec: Homepage Recent Posts Display
## ADDED Requirements
### Requirement: Display Recent Blog Posts on Homepage
The homepage MUST display the 3 most recent blog posts immediately after the user's description content section.
#### Scenario: Three most recent posts are displayed
**Given** the blog section contains 5 published posts
**When** a visitor accesses the homepage
**Then** exactly 3 most recent posts are displayed
**And** posts are ordered by publication date (newest first)
#### Scenario: Fewer than three posts exist
**Given** the blog section contains only 2 published posts
**When** a visitor accesses the homepage
**Then** all 2 posts are displayed
**And** no error or empty slots are shown
#### Scenario: No blog posts exist
**Given** the blog section contains no published posts
**When** a visitor accesses the homepage
**Then** the recent posts section is not displayed
**And** the homepage layout remains intact
### Requirement: Post Display Format
Each displayed blog post MUST show the post title as a clickable link and the publication date.
#### Scenario: Post title is clickable
**Given** a blog post is displayed on the homepage
**When** a visitor clicks the post title
**Then** they are navigated to the full blog post page
**And** the language context is preserved (German homepage → German post, English homepage → English post)
#### Scenario: Publication date is formatted correctly
**Given** a blog post with date "2025-10-30" is displayed
**When** viewing the German homepage
**Then** the date is formatted as "30. October 2025"
**When** viewing the English homepage
**Then** the date is formatted as "October 30, 2025"
### Requirement: Multilingual Support
The recent posts section MUST respect the site's multilingual configuration and display language-appropriate content.
#### Scenario: German homepage shows German posts
**Given** the visitor accesses the German homepage (/de/)
**When** the recent posts section is rendered
**Then** only German blog posts are displayed
**And** the section heading is in German (e.g., "Neueste Beiträge")
#### Scenario: English homepage shows English posts
**Given** the visitor accesses the English homepage (/en/)
**When** the recent posts section is rendered
**Then** only English blog posts are displayed
**And** the section heading is in English (e.g., "Recent Posts")
### Requirement: Visual Design and Layout
The recent posts section MUST maintain consistency with the existing site design and remain responsive.
#### Scenario: Section styling matches site design
**Given** the recent posts section is displayed
**Then** it uses Bootstrap classes consistent with the rest of the site
**And** spacing and typography match existing content sections
**And** the section is visually distinct from the description section above
#### Scenario: Responsive layout on mobile devices
**Given** the homepage is viewed on a mobile device (< 768px width)
**When** the recent posts section is displayed
**Then** the layout remains readable and properly formatted
**And** no horizontal scrolling is required
**And** links are easily tappable
### Requirement: Performance and Build
The implementation MUST not negatively impact Hugo build performance or site load times.
#### Scenario: Hugo build completes successfully
**Given** the homepage template includes recent posts query
**When** running `hugo build`
**Then** the build completes without errors or warnings
**And** the homepage HTML is generated correctly for both languages
#### Scenario: Site performance remains optimal
**Given** the homepage includes recent posts section
**When** measuring page load performance
**Then** Lighthouse performance score remains >90
**And** no additional HTTP requests are made
**And** HTML file size increase is minimal (< 2KB)
@@ -0,0 +1,78 @@
# Tasks: Display Recent Blog Posts on Homepage
## Implementation Tasks
### 1. Update Homepage Template
- [x] Modify `layouts/index.html` to query recent blog posts
- [x] Add Hugo template logic to fetch 3 most recent posts from blog section
- [x] Filter posts by current language context
- [x] Sort posts by publication date (descending)
**Validation:**
- Template syntax is correct (no Hugo build errors)
- Recent posts query respects language context
- Exactly 3 posts are retrieved (or fewer if less exist)
### 2. Implement Recent Posts Section UI
- [x] Add semantic HTML structure for recent posts section
- [x] Include section heading (translatable: "Neueste Beiträge" / "Recent Posts")
- [x] Display each post as list item with title link and date
- [x] Apply Bootstrap classes for consistent styling
- [x] Add appropriate spacing after description section
**Validation:**
- Section heading displays correctly in both languages
- Post titles are clickable links using `relPermalink`
- Dates are formatted using Hugo's date formatting
- Bootstrap classes match existing site patterns
- Visual spacing separates description from recent posts
### 3. Add Multilingual Section Headings
- [x] Implement conditional heading based on language context
- [x] German: "Neueste Beiträge" or "Aktuelle Artikel"
- [x] English: "Recent Posts" or "Latest Articles"
- [x] Use Hugo's language detection (`{{ .Language.Lang }}`)
**Validation:**
- German homepage shows German heading
- English homepage shows English heading
- Heading style matches other section headings on the site
### 4. Test and Verify
- [x] Build site with `hugo` command
- [x] Verify German homepage (/de/) displays recent German posts
- [x] Verify English homepage (/en/) displays recent English posts
- [x] Test with different numbers of blog posts (0, 1, 2, 3, 5+)
- [x] Check responsive layout on mobile viewport
- [x] Verify all links navigate correctly
**Validation:**
- Hugo build completes without errors
- Both language versions render correctly
- Edge cases handled (0 posts, < 3 posts)
- Links preserve language context
- Layout is responsive and accessible
### 5. Performance Check
- [x] Measure HTML file size increase
- [x] Verify no new HTTP requests added
- [x] Confirm build time impact is negligible
- [x] Optional: Run Lighthouse audit
**Validation:**
- File size increase < 2KB
- No additional network requests
- Build time difference < 100ms
- Performance score remains high
## Dependencies
- Task 1 must complete before Task 2
- Task 2 and 3 can be done in parallel or sequentially
- Task 4 requires all implementation tasks (1-3) complete
- Task 5 can be done after Task 4
## Notes
- Keep implementation minimal and focused on title + date only
- Use existing blog post querying patterns if any exist in the codebase
- Ensure null/empty checks to handle cases with no blog posts
- Consider using Hugo's `.Site.RegularPages` or `.Site.Taxonomies` for blog queries
@@ -0,0 +1,51 @@
# Proposal: Add Dialogbasierte Anwendungen Blog Post
## Context
Create a new bilingual blog post based on Notion content about dialog-based applications and language model interactions. The post explores how modern language models enable Star Trek-like computer interactions and includes a practical experiment with a member management system.
## Motivation
This blog post:
- Shares a practical exploration of dialog-based interfaces using language models
- Documents a real-world experiment with MCP and GraphQL
- Demonstrates the potential of conversational UI for business applications
- Provides both German and English versions for broader reach
## Scope
### In Scope
- Create new blog post page bundle under `content/blog/dialogbasierte-anwendungen/`
- German version (original from Notion)
- English version (AI-translated)
- Proper frontmatter with title, date, description
- All external links and references preserved
- Validation that post renders correctly in Hugo
### Out of Scope
- No changes to existing blog posts
- No template or layout modifications
- No Hugo configuration changes
- No new images or assets (uses external links)
## Dependencies
None. This is a new content addition that doesn't affect existing functionality.
## Risks
Low risk. Adding new content only, no structural changes.
## Success Criteria
- Blog post exists in both German and English
- Content accurately reflects Notion source
- English translation is natural and maintains meaning
- Hugo builds successfully
- Post displays correctly in both languages
- All links are functional
- OpenSpec validation passes
## Related Changes
None. First implementation of blog post content addition workflow.
@@ -0,0 +1,167 @@
# Spec: Blog Post - Dialogbasierte Anwendungen
## Overview
Specification for the bilingual blog post "Computer, ..." about dialog-based applications using language models. Uses translated URL slugs with separate directories linked via translationKey.
## ADDED Requirements
### Requirement: Blog post directory structure with translated slugs
The blog post MUST exist in separate directories for German and English with appropriate URL slugs.
**Rationale**: Using translated slugs provides better SEO and user experience, with language-appropriate URLs.
#### Scenario: Directories created with translated slugs
**Given** the Hugo content directory structure
**When** creating the new blog post
**Then** a directory `content/blog/dialogbasierte-anwendungen/` must exist for German
**And** a directory `content/blog/dialog-based-applications/` must exist for English
**And** German directory must contain `index.de.md`
**And** English directory must contain `index.en.md`
**And** both files must include `translationKey: "dialogbasierte-anwendungen-post"` in frontmatter
---
### Requirement: German blog post content
The German version MUST accurately represent the Notion source content.
**Rationale**: Preserve the original German text exactly as written by the author.
#### Scenario: German content matches Notion source
**Given** the Notion page content at https://www.notion.so/Computer-2b5c4dc46de08053954ff3a14b171b47
**When** creating the German blog post file
**Then** the content must match the Notion source text
**And** all formatting (headings, lists, emphasis) must be preserved
**And** all external links must be preserved
**And** the TL;DR section must be included
**And** the Codeberg repository link must be included
#### Scenario: German frontmatter is correct
**Given** the blog post requirements
**When** creating the German blog post
**Then** the frontmatter must use YAML format (three dashes)
**And** must include `title: "Computer, …"`
**And** must include `date: 2025-11-25`
**And** must include `translationKey: "dialogbasierte-anwendungen-post"`
**And** must include a `description` field with appropriate summary text
**And** must be valid YAML syntax
---
### Requirement: English blog post translation
The English version MUST be a natural, accurate translation of the German content.
**Rationale**: Make the content accessible to English-speaking readers while maintaining technical accuracy and readability.
#### Scenario: English translation is natural and accurate
**Given** the German blog post content
**When** creating the English translation
**Then** the translation must convey the same meaning as the German original
**And** must use natural English phrasing and idioms
**And** must maintain technical accuracy for all concepts
**And** must preserve the same structure (sections, lists)
**And** Star Trek references must be preserved (universally known)
#### Scenario: English frontmatter is correct
**Given** the blog post requirements
**When** creating the English blog post
**Then** the frontmatter must use YAML format (three dashes)
**And** must include `title` with natural English translation
**And** must include `date: 2025-11-25` (same as German)
**And** must include `translationKey: "dialogbasierte-anwendungen-post"`
**And** must include a `description` field with English summary
**And** must be valid YAML syntax
#### Scenario: External links are adapted appropriately
**Given** external reference links in the German version
**When** creating the English translation
**Then** Wikipedia links should point to English Wikipedia versions where applicable
**And** code repository links (Codeberg) must remain unchanged
**And** all links must be functional
---
### Requirement: Content formatting and structure
Both versions MUST use proper markdown formatting consistent with Hugo blog posts.
**Rationale**: Ensure consistent rendering and readability across the blog.
#### Scenario: Markdown structure is correct
**Given** the blog post content
**When** creating the blog post files
**Then** headings must use proper markdown hierarchy (##, ###)
**And** lists must use proper markdown syntax
**And** links must use markdown link syntax `[text](url)`
**And** emphasis must use proper markdown (*italic*, **bold**)
**And** code references must use inline code backticks where appropriate
#### Scenario: Special characters are handled correctly
**Given** the blog post contains German umlauts and special characters
**When** creating the blog post files
**Then** files must be saved with UTF-8 encoding
**And** all special characters must render correctly (ä, ö, ü, ß, …)
---
### Requirement: Hugo build compatibility
The blog posts MUST build successfully with Hugo and render correctly.
**Rationale**: Ensure the content integrates properly with the existing Hugo site.
#### Scenario: Hugo builds successfully
**Given** the new blog post files
**When** running `hugo build`
**Then** the build must complete without errors
**And** no warnings related to the new blog post must be generated
#### Scenario: Blog posts are accessible at correct translated URLs
**Given** a successful Hugo build
**When** viewing the site
**Then** the German version must be accessible at `/de/blog/dialogbasierte-anwendungen/`
**And** the English version must be accessible at `/en/blog/dialog-based-applications/`
#### Scenario: Blog posts appear in blog listings
**Given** a successful Hugo build
**When** viewing the blog index pages
**Then** the new post must appear in the German blog listing at `/de/blog/`
**And** the new post must appear in the English blog listing at `/en/blog/`
**And** the post must display with correct title and description
#### Scenario: Language switcher works correctly
**Given** both language versions with matching translationKey
**When** viewing either version of the blog post
**Then** the language switcher must link to the corresponding translation
**And** switching from German to English must navigate to `/en/blog/dialog-based-applications/`
**And** switching from English to German must navigate to `/de/blog/dialogbasierte-anwendungen/`
---
### Requirement: Content validation
The implementation MUST pass OpenSpec validation.
**Rationale**: Ensure compliance with project standards and specification requirements.
#### Scenario: OpenSpec validation passes
**Given** the completed implementation
**When** running `openspec validate add-dialogbasierte-anwendungen-post --strict`
**Then** validation must pass with no errors
**And** no warnings should be generated
@@ -0,0 +1,36 @@
# Tasks: Add Dialogbasierte Anwendungen Blog Post
## Implementation Tasks
1. **Create blog post directory structure**
- Create `content/blog/dialogbasierte-anwendungen/` directory
- Set up page bundle for bilingual content
2. **Create German blog post**
- Create `content/blog/dialogbasierte-anwendungen/index.de.md`
- Add YAML frontmatter with title, date, description
- Transfer content from Notion source
- Preserve all formatting, links, and structure
- Verify markdown rendering
3. **Create English blog post**
- Create `content/blog/dialogbasierte-anwendungen/index.en.md`
- Add YAML frontmatter with translated title, date, description
- Create natural English translation maintaining technical accuracy
- Adapt Wikipedia links to English versions where applicable
- Verify markdown rendering
4. **Validate implementation**
- Run `openspec validate add-dialogbasierte-anwendungen-post --strict`
- Resolve any validation issues
5. **Test Hugo build**
- Run `hugo build`
- Verify no build errors
- Check both German and English versions render correctly
- Verify all links are functional
6. **Review and commit**
- Review generated content
- Create git commit with conventional commit message
- Ready for user review before merging to main
+83
View File
@@ -0,0 +1,83 @@
# blog-content Specification
## Purpose
TBD - created by archiving change add-blog-section. Update Purpose after archive.
## Requirements
### Requirement: Blog Section Organization
Blog posts SHALL be organized in a dedicated `content/blog/` directory following Hugo's content organization conventions.
#### Scenario: Blog section exists in content directory
**Given** the Hugo site structure
**When** examining the content directory
**Then** a `content/blog/` directory SHALL exist
**And** section index files `_index.de.md` and `_index.en.md` SHALL exist in `content/blog/`
**And** these index files SHALL contain appropriate front matter (title, description)
#### Scenario: Blog posts use page bundles
**Given** a blog post to be published
**When** adding the post to the site
**Then** the post SHALL be organized as a page bundle (directory with `index.de.md` or `index.en.md`)
**And** the bundle directory name SHALL be URL-friendly (lowercase, hyphens, no special characters)
**And** any post-specific assets (images, files) CAN be stored within the bundle directory
---
### Requirement: Blog Post Front Matter
Each blog post SHALL include properly structured front matter with essential metadata.
#### Scenario: Blog post front matter is complete
**Given** a blog post markdown file
**When** the post is processed by Hugo
**Then** the front matter SHALL include a `title` field
**And** the front matter SHALL include a `date` field in ISO 8601 format (YYYY-MM-DD)
**And** the front matter MAY include a `description` or `summary` field for excerpts
**And** the front matter MAY include a `draft` field to control publication status
#### Scenario: First blog post is ready
**Given** the Notion article "KI Generierte Website - ein Praxisbeispiel"
**When** converting it to Hugo format
**Then** a blog post SHALL exist at `content/blog/ki-generierte-website-praxisbeispiel/index.de.md`
**And** the post SHALL contain the full article content from Notion
**And** the front matter SHALL include title "KI Generierte Website - ein Praxisbeispiel"
**And** the front matter SHALL include an appropriate publication date
---
### Requirement: Multilingual Blog Support
Blog content SHALL support both German and English following the site's internationalization pattern.
#### Scenario: Blog section metadata is multilingual
**Given** the blog section
**When** accessing the blog in different languages
**Then** `content/blog/_index.de.md` SHALL exist with German metadata
**And** `content/blog/_index.en.md` SHALL exist with English metadata
**And** both SHALL have appropriate translated titles and descriptions
#### Scenario: Blog posts can exist in multiple languages
**Given** a blog post
**When** translations are available
**Then** the post MAY have both `index.de.md` and `index.en.md` in the same bundle
**And** Hugo SHALL serve the appropriate language version based on the URL path
**And** language switching SHALL work between translated blog posts
---
### Requirement: Content Formatting
Blog post content SHALL be written in Markdown with proper formatting support.
#### Scenario: Markdown content renders correctly
**Given** a blog post with Markdown content
**When** Hugo processes the post
**Then** headings (H1-H6) SHALL be rendered correctly
**And** paragraphs, lists, and emphasis SHALL be formatted properly
**And** code blocks with syntax highlighting SHALL be supported
**And** links and images SHALL be rendered correctly
**And** HTML content SHALL be allowed (Hugo's unsafe renderer is enabled in config)
#### Scenario: Special characters and formatting are preserved
**Given** blog post content with German special characters (ä, ö, ü, ß)
**When** the post is rendered
**Then** all special characters SHALL display correctly
**And** quotation marks and em dashes SHALL be preserved
**And** UTF-8 encoding SHALL be maintained throughout
+124
View File
@@ -0,0 +1,124 @@
# blog-templates Specification
## Purpose
TBD - created by archiving change add-blog-section. Update Purpose after archive.
## Requirements
### Requirement: Blog List Template
A blog list template SHALL display all published blog posts in a organized, user-friendly format.
#### Scenario: Blog list page renders with posts
**Given** published blog posts exist in `content/blog/`
**When** a user navigates to `/de/blog/` or `/en/blog/`
**Then** a blog list page SHALL be rendered using `layouts/blog/list.html`
**And** the page SHALL display all non-draft blog posts
**And** posts SHALL be sorted by date (most recent first)
#### Scenario: Blog list shows post metadata
**Given** the blog list page
**When** displaying each blog post entry
**Then** each entry SHALL show the post title as a link to the full post
**And** each entry SHALL show the publication date
**And** each entry MAY show a summary or excerpt of the post
**And** the title link SHALL navigate to the full blog post page
#### Scenario: Blog list is responsive
**Given** the blog list page
**When** viewed on different screen sizes
**Then** the layout SHALL be responsive using Bootstrap grid
**And** the page SHALL be readable on mobile devices (≥320px width)
**And** the page SHALL adapt to tablet and desktop viewports
**And** spacing and typography SHALL remain consistent with site design
---
### Requirement: Blog Post Single Template
Individual blog posts SHALL be displayed with a dedicated single post template providing optimal reading experience.
#### Scenario: Blog post page renders content
**Given** a blog post at `content/blog/ki-generierte-website-praxisbeispiel/index.de.md`
**When** a user navigates to `/de/blog/ki-generierte-website-praxisbeispiel/`
**Then** the post SHALL be rendered using `layouts/blog/single.html`
**And** the page SHALL display the post title as the main heading (H1)
**And** the page SHALL display the publication date
**And** the page SHALL display the full post content with proper formatting
#### Scenario: Blog post content is readable
**Given** a rendered blog post
**When** viewing the post content
**Then** headings SHALL use appropriate hierarchy (H2, H3, etc. for sections)
**And** paragraphs SHALL have readable line height and spacing
**And** code blocks SHALL be distinguishable with appropriate styling
**And** links SHALL be clearly identifiable and accessible
**And** images SHALL be responsive and properly sized
#### Scenario: Blog post has navigation
**Given** a blog post page
**When** a user finishes reading
**Then** the page SHALL include a link back to the blog list
**And** the navigation link SHALL be clearly visible
**And** the site's main navigation SHALL remain accessible
---
### Requirement: Blog Navigation Integration
The blog section SHALL be accessible through the site's main navigation.
#### Scenario: Blog link in main navigation
**Given** the site configuration
**When** rendering the main navigation menu
**Then** a "Blog" link SHALL appear in the navigation for German pages
**And** a "Blog" link SHALL appear in the navigation for English pages
**And** the link SHALL point to `/de/blog/` for German
**And** the link SHALL point to `/en/blog/` for English
#### Scenario: Active state for blog pages
**Given** a user is on a blog page (list or single post)
**When** viewing the navigation menu
**Then** the "Blog" link SHALL have an active state indicator
**And** the active state SHALL use the same styling as other active navigation items
**And** the active state SHALL be maintained on both blog list and single post pages
---
### Requirement: Blog Template Semantic HTML
Blog templates SHALL use semantic HTML5 elements for accessibility and SEO.
#### Scenario: Blog list uses semantic structure
**Given** the blog list template
**When** rendering the page
**Then** the main content SHALL be wrapped in a `<main>` element
**And** individual blog post entries SHALL use `<article>` elements
**And** post metadata SHALL use appropriate semantic tags (e.g., `<time>` for dates)
**And** heading hierarchy SHALL be logical and sequential
#### Scenario: Blog post uses article structure
**Given** a blog post template
**When** rendering the page
**Then** the post SHALL be wrapped in an `<article>` element
**And** the post SHALL have a `<header>` with title and metadata
**And** the post content SHALL be in a `<div>` or `<section>` with clear semantic structure
**And** all headings SHALL follow proper hierarchy
---
### Requirement: Blog Styling Consistency
Blog templates SHALL maintain visual consistency with the rest of the site.
#### Scenario: Blog pages use site styling
**Given** any blog page (list or single)
**When** the page is rendered
**Then** the page SHALL use the site's base layout (`baseof.html`)
**And** the page SHALL include the site's header and footer partials
**And** the page SHALL use Bootstrap classes for styling
**And** typography SHALL be consistent with other site pages
**And** color scheme SHALL match the site's design
#### Scenario: Blog pages are accessible
**Given** any blog page
**When** evaluated for accessibility
**Then** the page SHALL meet WCAG 2.1 AA standards
**And** all interactive elements SHALL be keyboard accessible
**And** color contrast ratios SHALL meet minimum requirements
**And** images SHALL have appropriate alt text (if any)
**And** the page SHALL have appropriate semantic structure for screen readers
+39
View File
@@ -0,0 +1,39 @@
# content-management Specification
## Purpose
TBD - created by archiving change add-cv-page. Update Purpose after archive.
## Requirements
### Requirement: CV Page Content
The site SHALL provide a dedicated curriculum vitae page that explains how to securely download the CV document and request additional materials.
#### Scenario: CV page displays download instructions
- **WHEN** a visitor navigates to `/cv/` or clicks "Lebenslauf" in navigation
- **THEN** the page displays a clear explanation of the Tresorit download process
- **AND** the page includes the secure download link
- **AND** the page mentions email verification is required
- **AND** the page explains that certificates and references are available upon personal request
#### Scenario: CV page uses German language
- **WHEN** the CV page is rendered
- **THEN** all text is displayed in German
- **AND** the tone is professional and suitable for HR personnel and recruiters
#### Scenario: CV page follows site layout
- **WHEN** the CV page is rendered
- **THEN** it uses the standard single.html layout
- **AND** content is centered and readable
- **AND** it includes the site header and footer
### Requirement: CV Navigation Link
The site navigation SHALL include a link to the CV page.
#### Scenario: Navigation menu shows CV link
- **WHEN** a visitor views any page on the site
- **THEN** the navigation menu includes a "Lebenslauf" link
- **AND** the link points to `/cv/`
- **AND** the link follows the same styling as other navigation items
#### Scenario: CV link active state
- **WHEN** a visitor is on the CV page
- **THEN** the "Lebenslauf" navigation link shows active state styling
+86
View File
@@ -0,0 +1,86 @@
# content-migration Specification
## Purpose
TBD - created by archiving change migrate-site-content. Update Purpose after archive.
## Requirements
### Requirement: Content Extraction from Live Site
The system SHALL capture all text content, structure, and formatting from the live markusgraf.ch website to migrate into the Hugo project.
#### Scenario: Extract homepage content from live site
- **GIVEN** the live markusgraf.ch website HTML source is provided
- **WHEN** the HTML is parsed for content extraction
- **THEN** all text content, headings, and structure are captured
- **AND** the content is organized by logical sections (bio, intro, projects, etc.)
#### Scenario: Extract projects and portfolio content
- **GIVEN** the live site contains project or portfolio information
- **WHEN** projects are identified and extracted
- **THEN** each project has complete description captured
- **AND** project metadata and structure is documented
### Requirement: Content Conversion to Hugo Format
The system SHALL convert extracted HTML content into Hugo-compatible markdown format while preserving formatting and structure.
#### Scenario: Convert HTML to markdown
- **GIVEN** extracted HTML content from live site
- **WHEN** content is converted to markdown
- **THEN** all text formatting is preserved (bold, italic, links)
- **AND** HTML-specific elements are converted to markdown or Hugo shortcodes
- **AND** content follows markdown best practices
#### Scenario: Add Hugo front matter
- **GIVEN** converted markdown content
- **WHEN** content files are created
- **THEN** appropriate YAML front matter is added (title, description, date)
- **AND** front matter variables are correctly configured for templates
### Requirement: Homepage Content Integration
The system SHALL integrate migrated homepage content into content/_index.md, replacing placeholder content.
#### Scenario: Update homepage with actual content
- **GIVEN** converted homepage markdown content
- **WHEN** content/_index.md is updated
- **THEN** all placeholder text is replaced with actual content
- **AND** all sections from live site are present
- **AND** content hierarchy and flow matches live site structure
#### Scenario: Image references in content
- **GIVEN** homepage content references images
- **WHEN** image references are added to markdown
- **THEN** images use Hugo static path conventions (e.g., /images/photo.jpg)
- **AND** all image markdown syntax is correct
### Requirement: Content Structure Parity
The system SHALL ensure the Hugo site content structure matches the live markusgraf.ch site organization.
#### Scenario: Section organization matches live site
- **GIVEN** live site has distinct sections
- **WHEN** content is organized in Hugo
- **THEN** all sections are represented
- **AND** section order matches live site
- **AND** navigation between sections works correctly
#### Scenario: Multi-page structure if needed
- **GIVEN** live site has multiple pages
- **WHEN** pages are created in Hugo
- **THEN** each page has corresponding content file
- **AND** internal links between pages work
- **AND** navigation reflects page structure
### Requirement: Content Verification
The system SHALL verify that migrated content achieves parity with the live site.
#### Scenario: Content completeness check
- **GIVEN** Hugo site is built with migrated content
- **WHEN** compared with live markusgraf.ch
- **THEN** all text content from live site is present
- **AND** no content is missing or truncated
- **AND** content meaning and context are preserved
#### Scenario: Visual structure comparison
- **GIVEN** Hugo site is rendered
- **WHEN** viewed alongside live site
- **THEN** content sections appear in same order
- **AND** heading hierarchy matches
- **AND** overall content flow is equivalent
+100
View File
@@ -0,0 +1,100 @@
# deployment Specification
## Purpose
TBD - created by archiving change simple-deployment. Update Purpose after archive.
## Requirements
### Requirement: Rsync Deployment Script SHALL be provided
The system SHALL provide an automated deployment script that syncs the built Hugo site to the production server via rsync over SSH, properly mirroring content.
#### Scenario: User deploys site after building
**Given** the Hugo site has been built successfully (public/ directory exists)
**And** the user has set the SSH_USER and SSH_HOST environment variables
**When** the user runs the deployment script
**Then** the script connects to the specified SSH_HOST via rsync over SSH
**And** syncs all files from the public/ directory to the REMOTE_ROOT directory on the server
**And** deletes remote files that don't exist locally (mirror behavior)
**And** displays sync progress and completion status
**And** exits with status code 0 on success
#### Scenario: Script fails when credentials are missing
**Given** the Hugo site has been built
**And** the SSH_USER or SSH_HOST environment variable is not set
**When** the user runs the deployment script
**Then** the script displays an error message explaining the missing configuration
**And** exits with a non-zero status code
**And** does not attempt to connect to the server
#### Scenario: Script handles connection failures gracefully
**Given** the SSH_USER and SSH_HOST are set correctly
**And** the network connection to the server is unavailable or SSH authentication fails
**When** the user runs the deployment script
**Then** the script displays a clear error message about the connection failure
**And** exits with a non-zero status code
**And** does not leave the deployment in a partially completed state
### Requirement: Secure Credential Management SHALL be enforced
The deployment process SHALL handle credentials securely without exposing them in version control or script output.
#### Scenario: Configuration stored as environment variables
**Given** the user needs to deploy the site
**When** the user reviews the deployment documentation
**Then** the documentation instructs them to set SSH_USER, SSH_HOST, SSH_PORT, and REMOTE_ROOT as environment variables
**And** the deployment script reads configuration only from environment variables
**And** credentials are never hardcoded in scripts or configuration files
**And** SSH key-based authentication is used for secure, password-less access
#### Scenario: Script does not expose credentials in output
**Given** the deployment script is running
**When** the script displays status messages or logs
**Then** SSH keys or passwords are never displayed in plain text
**And** connection strings show only the host and user information
**And** error messages do not reveal credential values
### Requirement: Deployment Status Feedback SHALL be provided
The deployment script SHALL provide clear feedback about the deployment process and outcome.
#### Scenario: User receives progress updates during deployment
**Given** the deployment script is uploading files
**When** the upload is in progress
**Then** the script displays which files or directories are being uploaded
**And** shows overall progress indicators
**And** provides estimated time or completion percentage when possible
#### Scenario: Successful deployment confirmation
**Given** all files have been uploaded successfully
**When** the deployment completes
**Then** the script displays a success message
**And** confirms the total number of files uploaded
**And** exits with status code 0
#### Scenario: Failed deployment with actionable error
**Given** an error occurs during deployment
**When** the deployment fails
**Then** the script displays the specific error encountered
**And** suggests possible remediation steps
**And** exits with a non-zero status code
### Requirement: Deployment Prerequisites SHALL be verified
The deployment script SHALL verify that prerequisites are met before attempting deployment.
#### Scenario: Script checks for built site
**Given** the user runs the deployment script
**When** the script starts
**Then** it verifies that the public/ directory exists
**And** contains files to deploy
**And** exits with an error if the directory is missing or empty
#### Scenario: Script checks for required tools
**Given** the deployment script starts
**When** it performs prerequisite checks
**Then** it verifies that rsync is installed and available
**And** displays an installation message if rsync is missing
**And** exits with an error if required tools are unavailable
#### Scenario: Script supports configurable ports and remote directories
**Given** the user needs to deploy to a non-standard SSH port or directory
**When** the user sets SSH_PORT and REMOTE_ROOT environment variables
**Then** the script uses the specified port instead of the default (22)
**And** the script uploads to the specified remote directory instead of the default (/httpsdocs)
**And** if these variables are not set, the script uses sensible defaults
@@ -0,0 +1,89 @@
# homepage-recent-posts Specification
## Purpose
TBD - created by archiving change homepage-recent-posts. Update Purpose after archive.
## Requirements
### Requirement: Display Recent Blog Posts on Homepage
The homepage MUST display the 3 most recent blog posts immediately after the user's description content section.
#### Scenario: Three most recent posts are displayed
**Given** the blog section contains 5 published posts
**When** a visitor accesses the homepage
**Then** exactly 3 most recent posts are displayed
**And** posts are ordered by publication date (newest first)
#### Scenario: Fewer than three posts exist
**Given** the blog section contains only 2 published posts
**When** a visitor accesses the homepage
**Then** all 2 posts are displayed
**And** no error or empty slots are shown
#### Scenario: No blog posts exist
**Given** the blog section contains no published posts
**When** a visitor accesses the homepage
**Then** the recent posts section is not displayed
**And** the homepage layout remains intact
### Requirement: Post Display Format
Each displayed blog post MUST show the post title as a clickable link and the publication date.
#### Scenario: Post title is clickable
**Given** a blog post is displayed on the homepage
**When** a visitor clicks the post title
**Then** they are navigated to the full blog post page
**And** the language context is preserved (German homepage → German post, English homepage → English post)
#### Scenario: Publication date is formatted correctly
**Given** a blog post with date "2025-10-30" is displayed
**When** viewing the German homepage
**Then** the date is formatted as "30. October 2025"
**When** viewing the English homepage
**Then** the date is formatted as "October 30, 2025"
### Requirement: Multilingual Support
The recent posts section MUST respect the site's multilingual configuration and display language-appropriate content.
#### Scenario: German homepage shows German posts
**Given** the visitor accesses the German homepage (/de/)
**When** the recent posts section is rendered
**Then** only German blog posts are displayed
**And** the section heading is in German (e.g., "Neueste Beiträge")
#### Scenario: English homepage shows English posts
**Given** the visitor accesses the English homepage (/en/)
**When** the recent posts section is rendered
**Then** only English blog posts are displayed
**And** the section heading is in English (e.g., "Recent Posts")
### Requirement: Visual Design and Layout
The recent posts section MUST maintain consistency with the existing site design and remain responsive.
#### Scenario: Section styling matches site design
**Given** the recent posts section is displayed
**Then** it uses Bootstrap classes consistent with the rest of the site
**And** spacing and typography match existing content sections
**And** the section is visually distinct from the description section above
#### Scenario: Responsive layout on mobile devices
**Given** the homepage is viewed on a mobile device (< 768px width)
**When** the recent posts section is displayed
**Then** the layout remains readable and properly formatted
**And** no horizontal scrolling is required
**And** links are easily tappable
### Requirement: Performance and Build
The implementation MUST not negatively impact Hugo build performance or site load times.
#### Scenario: Hugo build completes successfully
**Given** the homepage template includes recent posts query
**When** running `hugo build`
**Then** the build completes without errors or warnings
**And** the homepage HTML is generated correctly for both languages
#### Scenario: Site performance remains optimal
**Given** the homepage includes recent posts section
**When** measuring page load performance
**Then** Lighthouse performance score remains >90
**And** no additional HTTP requests are made
**And** HTML file size increase is minimal (< 2KB)
+133
View File
@@ -0,0 +1,133 @@
# hugo-site Specification
## Purpose
TBD - created by archiving change add-minimal-hugo-site. Update Purpose after archive.
## Requirements
### Requirement: Hugo Project Structure
The system SHALL initialize a Hugo static site with the standard directory structure including layouts, content, static, and data directories.
#### Scenario: Hugo site initialized
- **WHEN** Hugo site is created
- **THEN** the following directories exist: layouts/, content/, static/, data/
- **AND** a hugo.toml configuration file is present at the root
#### Scenario: Hugo builds successfully
- **WHEN** running `hugo` command
- **THEN** the site builds without errors
- **AND** generates static HTML files in the public/ directory
### Requirement: Base Layout Template
The system SHALL provide a baseof.html template that defines the common HTML structure for all pages, including Bootstrap 5.x integration.
#### Scenario: Base layout includes Bootstrap
- **WHEN** any page is rendered
- **THEN** the HTML output includes Bootstrap 5.x CSS from CDN
- **AND** the page has proper HTML5 doctype and meta tags
- **AND** the page is responsive with Bootstrap's viewport meta tag
#### Scenario: Base layout includes header and footer
- **WHEN** any page is rendered
- **THEN** the page includes the header partial
- **AND** the page includes the footer partial
- **AND** the main content block is properly positioned between them
### Requirement: Reusable Partials
The system SHALL provide reusable partial templates for header, footer, and navigation components.
#### Scenario: Header partial exists
- **WHEN** baseof.html calls the header partial
- **THEN** the header is rendered with consistent styling across all pages
#### Scenario: Footer partial exists
- **WHEN** baseof.html calls the footer partial
- **THEN** the footer is rendered with consistent styling across all pages
#### Scenario: Navigation partial exists
- **WHEN** header includes navigation
- **THEN** the navigation menu renders with links to main pages
- **AND** uses Bootstrap navigation components
### Requirement: Homepage Template
The system SHALL provide an index.html template for the homepage that extends baseof.html and displays the main landing content.
#### Scenario: Homepage renders correctly
- **WHEN** accessing the root URL
- **THEN** the homepage template is used
- **AND** displays content from content/_index.md
- **AND** matches the structure of the existing markusgraf.ch homepage
### Requirement: Single Page Template
The system SHALL provide a default single.html template for individual content pages.
#### Scenario: Single page renders correctly
- **WHEN** accessing any content page
- **THEN** the single page template is used
- **AND** displays the page title
- **AND** renders the markdown content as HTML
### Requirement: Static Assets
The system SHALL serve static assets (images, fonts, CSS files) from the static/ directory.
#### Scenario: Static files are accessible
- **WHEN** a static file is placed in static/
- **THEN** it is accessible at the site root in the built site
- **AND** preserves the directory structure from static/
### Requirement: Content Management
The system SHALL support markdown files in the content/ directory that are rendered into HTML pages.
#### Scenario: Markdown content is rendered
- **WHEN** a markdown file exists in content/
- **THEN** Hugo processes it into an HTML page
- **AND** front matter variables are accessible in templates
#### Scenario: Page metadata
- **WHEN** a content file has front matter
- **THEN** title, date, and other metadata are available in templates
- **AND** can be used for page titles and navigation
### Requirement: Configuration
The system SHALL use hugo.toml for site configuration including baseURL, title, and language settings.
#### Scenario: Site configuration is applied
- **WHEN** hugo.toml contains site settings
- **THEN** those settings are used during site generation
- **AND** site title appears in page titles
- **AND** baseURL is used for absolute URLs
### Requirement: Responsive Design
The system SHALL render pages that are responsive and mobile-friendly using Bootstrap's grid system and responsive utilities.
#### Scenario: Mobile viewport
- **WHEN** viewing the site on mobile devices
- **THEN** the layout adapts to small screens
- **AND** navigation is accessible
- **AND** content is readable without horizontal scrolling
#### Scenario: Tablet and desktop viewports
- **WHEN** viewing the site on larger screens
- **THEN** the layout utilizes available space appropriately
- **AND** maintains readability and visual hierarchy
### Requirement: HTML Validity
The system SHALL generate valid HTML5 markup that passes standard validation.
#### Scenario: Valid HTML output
- **WHEN** pages are generated
- **THEN** HTML is well-formed
- **AND** includes required DOCTYPE and meta tags
- **AND** uses semantic HTML5 elements where appropriate
### Requirement: Accessibility Basics
The system SHALL implement basic accessibility features including semantic HTML and proper heading hierarchy.
#### Scenario: Semantic HTML structure
- **WHEN** pages are rendered
- **THEN** content uses appropriate semantic elements (header, nav, main, footer, article)
- **AND** maintains logical heading hierarchy (h1, h2, h3)
#### Scenario: Navigation accessibility
- **WHEN** keyboard navigation is used
- **THEN** all interactive elements are focusable
- **AND** focus order is logical
+90
View File
@@ -0,0 +1,90 @@
# image-assets Specification
## Purpose
TBD - created by archiving change migrate-site-content. Update Purpose after archive.
## Requirements
### Requirement: Image Asset Discovery
The system SHALL identify and catalog all images from the live markusgraf.ch website for migration.
#### Scenario: Identify all images on live site
- **GIVEN** the live markusgraf.ch website HTML source
- **WHEN** images are catalogued from the HTML
- **THEN** all image URLs are documented
- **AND** image purposes (profile, project, icon, etc.) are identified
- **AND** image file formats and sizes are noted
#### Scenario: Create asset inventory
- **GIVEN** identified images from live site
- **WHEN** asset inventory is created
- **THEN** a complete list of images with URLs exists
- **AND** each image is categorized by type/purpose
- **AND** inventory documents source URLs for downloading
### Requirement: Image Download and Organization
The system SHALL download images from the live site and organize them in Hugo's static directory following logical grouping principles.
#### Scenario: Download images from live site
- **GIVEN** list of image URLs from inventory
- **WHEN** images are downloaded
- **THEN** all images are successfully retrieved
- **AND** image files are verified for integrity
- **AND** no corrupted or failed downloads exist
#### Scenario: Organize images in static directory
- **GIVEN** downloaded images
- **WHEN** images are placed in Hugo project
- **THEN** images are saved to static/images/ or appropriate subdirectories
- **AND** directory structure reflects logical grouping (e.g., static/images/projects/, static/images/profile/)
- **AND** filenames are consistent and descriptive
### Requirement: Image Reference Integration
The system SHALL update all image references in content files to use Hugo's static path conventions correctly.
#### Scenario: Update image paths in content
- **GIVEN** images are in Hugo static directory
- **WHEN** content files reference images
- **THEN** image paths use Hugo static conventions (e.g., /images/photo.jpg)
- **AND** all image references use correct relative or absolute paths
- **AND** markdown image syntax is properly formatted
#### Scenario: Verify image links resolve
- **GIVEN** Hugo site is built
- **WHEN** pages with images are rendered
- **THEN** all image references resolve correctly
- **AND** no broken image links exist
- **AND** no 404 errors occur for image resources
### Requirement: Image Display Verification
The system SHALL ensure all migrated images display correctly in the built Hugo site.
#### Scenario: Images render correctly
- **GIVEN** Hugo site is built and served
- **WHEN** pages with images are viewed
- **THEN** all images display visually
- **AND** image aspect ratios are appropriate
- **AND** images load from correct static paths
- **AND** no missing or placeholder images appear
#### Scenario: Image dimensions and quality
- **GIVEN** images are displayed on site
- **WHEN** comparing to live site
- **THEN** image sizes are comparable to originals
- **AND** image quality is maintained
- **AND** no distortion or stretching occurs
### Requirement: Image Accessibility
The system SHALL ensure all images have appropriate accessibility attributes for screen readers and assistive technologies.
#### Scenario: Alt text for all images
- **GIVEN** images in content files
- **WHEN** markdown image syntax is used
- **THEN** all images include descriptive alt text
- **AND** alt text meaningfully describes image content
- **AND** decorative images use empty alt text where appropriate
#### Scenario: Semantic image usage
- **GIVEN** images serve specific purposes
- **WHEN** images are integrated into content
- **THEN** images are used semantically (figures, illustrations, etc.)
- **AND** image context is clear from surrounding content
+120
View File
@@ -0,0 +1,120 @@
# internationalization Specification
## Purpose
TBD - created by archiving change add-multilingual-support. Update Purpose after archive.
## Requirements
### Requirement: Multilingual Content Support
The site SHALL support multiple languages with German (de) as the default language and English (en) as a secondary language.
#### Scenario: German content is accessible at /de/ path
- **WHEN** a visitor navigates to `/de/` or any `/de/*` path
- **THEN** the page displays in German language
- **AND** all navigation, content, and UI elements are in German
#### Scenario: English content is accessible at /en/ path
- **WHEN** a visitor navigates to `/en/` or any `/en/*` path
- **THEN** the page displays in English language
- **AND** all navigation, content, and UI elements are in English
#### Scenario: All pages have translations
- **WHEN** content exists in one language
- **THEN** a corresponding translation SHALL exist in the other language
- **AND** the content structure mirrors across languages (e.g., `/de/cv``/en/cv`)
### Requirement: Language Switcher
The site navigation SHALL include a language switcher that allows users to change languages while maintaining page context.
#### Scenario: Language switcher is visible in navigation
- **WHEN** a visitor views any page
- **THEN** the navigation displays language options (DE, EN)
- **AND** the current active language is visually indicated
- **AND** the inactive language is shown as a clickable link
#### Scenario: Language switching maintains page context
- **WHEN** a visitor is on `/de/cv` and clicks the English language link
- **THEN** the page navigates to `/en/cv` (the English version of the same page)
- **AND** the page content changes to English
#### Scenario: Language switching from home page
- **WHEN** a visitor is on `/de/` and clicks the English language link
- **THEN** the page navigates to `/en/`
- **AND** the home page content changes to English
#### Scenario: Language switcher handles missing translations
- **WHEN** a visitor is on a page without a translation
- **THEN** the language link points to the home page of the target language
- **AND** the user is not left on a broken or non-existent page
### Requirement: Language Persistence
The site SHALL maintain the user's language preference through URL-based routing without requiring cookies or JavaScript.
#### Scenario: Language preference persists across navigation
- **WHEN** a visitor navigates to `/de/cv` and then clicks a navigation link
- **THEN** all subsequent pages remain in German (e.g., clicking "Home" goes to `/de/`)
- **AND** the language preference is maintained through the URL structure
#### Scenario: Direct URL access respects language
- **WHEN** a visitor directly accesses `/en/cv` via URL or bookmark
- **THEN** the page displays in English
- **AND** all navigation links point to English pages
### Requirement: Browser Language Detection
The site configuration SHALL support automatic language selection based on browser language preferences for first-time visitors.
#### Scenario: Server redirect based on Accept-Language header
- **WHEN** a visitor accesses the root URL `/` for the first time
- **THEN** the server MAY redirect to `/de/` or `/en/` based on the browser's `Accept-Language` header
- **AND** German is used as the fallback if no language preference is detected
#### Scenario: URL-based access bypasses language detection
- **WHEN** a visitor directly accesses `/de/` or `/en/`
- **THEN** the specified language is used regardless of browser settings
- **AND** no redirect occurs
### Requirement: Content Translation
All site content SHALL be translated between German and English languages.
#### Scenario: Home page content is translated
- **WHEN** viewing the home page in German (`/de/`)
- **THEN** all text including introduction, project descriptions, and UI labels are in German
- **WHEN** viewing the home page in English (`/en/`)
- **THEN** all text including introduction, project descriptions, and UI labels are in English
#### Scenario: CV page content is translated
- **WHEN** viewing the CV page in German (`/de/cv`)
- **THEN** all text including headings, instructions, and contact information are in German
- **WHEN** viewing the CV page in English (`/en/cv`)
- **THEN** all text including headings, instructions, and contact information are in English
#### Scenario: Navigation menu is translated
- **WHEN** viewing any page in German
- **THEN** navigation menu items are in German (e.g., "Lebenslauf" for CV)
- **WHEN** viewing any page in English
- **THEN** navigation menu items are in English (e.g., "Curriculum Vitae" for CV)
#### Scenario: Footer content is translated
- **WHEN** viewing any page in German
- **THEN** footer content including address and contact information labels are in German
- **WHEN** viewing any page in English
- **THEN** footer content including address and contact information labels are in English
### Requirement: SEO and Accessibility
The site SHALL implement proper multilingual SEO practices and accessibility features.
#### Scenario: HTML lang attribute is set correctly
- **WHEN** viewing a German page
- **THEN** the HTML element has `lang="de"` attribute
- **WHEN** viewing an English page
- **THEN** the HTML element has `lang="en"` attribute
#### Scenario: Alternative language links are provided
- **WHEN** viewing any page
- **THEN** the HTML head includes `<link rel="alternate" hreflang="de" ...>` and `<link rel="alternate" hreflang="en" ...>` tags
- **AND** these tags point to the corresponding page in each language
#### Scenario: Page titles reflect language
- **WHEN** viewing a page in German
- **THEN** the page title is in German
- **WHEN** viewing a page in English
- **THEN** the page title is in English
+69
View File
@@ -0,0 +1,69 @@
# styling Specification
## Purpose
TBD - created by archiving change add-custom-bootstrap-styling. Update Purpose after archive.
## Requirements
### Requirement: SCSS Processing Pipeline
The system SHALL process SCSS files using Hugo Pipes to generate custom CSS that supplements Bootstrap's CDN-provided styles.
#### Scenario: SCSS compilation on build
- **WHEN** Hugo builds the site
- **THEN** SCSS files in `assets/scss/` are compiled to CSS
- **AND** the compiled CSS is fingerprinted for cache busting
- **AND** the resulting CSS file is included in the HTML output
#### Scenario: Development mode with live reload
- **WHEN** running Hugo in development mode (hugo server)
- **THEN** SCSS changes trigger automatic recompilation
- **AND** the browser live-reloads with updated styles
### Requirement: Bootstrap Variable Customization
The system SHALL allow customization of Bootstrap design tokens through SCSS variables without modifying the CDN-provided Bootstrap CSS.
#### Scenario: Override heading font sizes
- **WHEN** custom heading sizes are defined in SCSS variables
- **THEN** the compiled CSS applies these sizes to heading elements
- **AND** the Bootstrap CDN CSS is loaded first (base styles)
- **AND** custom CSS is loaded after (overrides)
#### Scenario: Override link colors
- **WHEN** custom link colors are defined in SCSS variables
- **THEN** links across the site use the custom colors
- **AND** link hover states use appropriate custom colors
#### Scenario: Override spacing scale
- **WHEN** custom spacing values are defined
- **THEN** elements using those spacing classes reflect the custom values
### Requirement: Custom Style Organization
The system SHALL organize custom styles in a maintainable SCSS file structure following Hugo and SASS best practices.
#### Scenario: Main SCSS entry point
- **WHEN** Hugo processes SCSS
- **THEN** a single main SCSS file serves as the entry point
- **AND** this file imports Bootstrap variable overrides
- **AND** this file imports custom component styles
- **AND** this file imports custom utility styles
#### Scenario: Modular SCSS files
- **WHEN** developers add new custom styles
- **THEN** styles can be organized into separate partial files
- **AND** partial files are imported into the main SCSS file
- **AND** file naming follows SCSS conventions (e.g., `_variables.scss`, `_custom.scss`)
### Requirement: CSS Output Integration
The system SHALL integrate compiled custom CSS into the base layout template alongside Bootstrap CDN CSS.
#### Scenario: CSS load order
- **WHEN** a page is rendered
- **THEN** Bootstrap CDN CSS loads first
- **AND** custom compiled CSS loads second
- **AND** CSS files include integrity hashes where applicable
- **AND** the custom CSS file path includes a fingerprint for cache busting
#### Scenario: Production build optimization
- **WHEN** building for production
- **THEN** SCSS is compiled with minification
- **AND** the output CSS is optimized for file size
- **AND** source maps are not included in production builds
+18
View File
@@ -0,0 +1,18 @@
# Markus Graf
> Persönliche Website von Markus Graf - Entwickler, BSc Psychologie, Berater.
> Personal website of Markus Graf - Developer, BSc Psychology, Consultant.
## Profile
- [Lebenslauf (DE)](https://markusgraf.ch/de/cv/)
- [Curriculum Vitae (EN)](https://markusgraf.ch/en/cv/)
## Blog
- [Dialogbasierte Anwendungen (DE)](https://markusgraf.ch/de/blog/dialogbasierte-anwendungen/)
- [Dialog-based Applications (EN)](https://markusgraf.ch/en/blog/dialog-based-applications/)
- [KI-Generierte Website Praxisbeispiel (DE)](https://markusgraf.ch/de/blog/ki-generierte-website-praxisbeispiel/)
- [AI Generated Website Case Study (EN)](https://markusgraf.ch/en/blog/ai-generated-website-case-study/)
- [Ubuntu für Kinder (DE)](https://markusgraf.ch/de/blog/ubuntu-fuer-kinder/)
- [Ubuntu for Kids (EN)](https://markusgraf.ch/en/blog/ubuntu-for-kids/)