Work

Everything I’ve built.

Client work, a bachelor thesis that went live in a factory, and the tools I build for myself. Pick one to read the detail.

← All projects

Shipped

Atlassian
AI Ecosystem

Company : Communardo GmbH Stack : Rovo · Forge Year : Aug 2024 – Aug 2025

Communardo is a Platinum Atlassian Partner and runs on Atlassian itself. Everything I built there was internal: documentation, wiki, knowledge management, and agents built into the processes that already existed rather than next to them.

What I did

  • Around 10 Rovo agents across QM, workflow management and audit processes. Another 10 later at Uhlmann. Not all of them made it into production.
  • A meta-agent that lets non-technical teams build their own agents
  • Audit automation: a multi-hour process down to 10 minutes
  • 2 live-coding talks at ACE events, 55+ practitioners incl. C-level

The agent that builds agents

Teams knew what they wanted but not how to ask for it, and a badly written system prompt produces a badly behaved agent. So the meta-agent runs the prompt engineering itself, in the background. It interviews the user until it has what they actually meant rather than what they first said, checks those answers against the internal wiki, and links the pages that matter straight into the system prompt. The agents built this way outperformed the standard configuration, and what made the difference was the context, not the wording.

The audit

The old version was a person reading through the wiki looking for gaps. The agent reads the documentation, flags what is open and what is missing against ISO, turns each finding into a Jira ticket and assigns it to whoever owns that area. The output is not a report, it is work with a name on it.

What I took away

Nobody was hiding anything. The findings were sitting in the wiki the whole time, in prose, across hundreds of pages, and nobody had hours to read all of it. Documentation is not knowledge until something reads it.

← All projects

← All projects

Bachelor Thesis

Intelligent
Process
Automation

Client : Uhlmann Pac-Systeme Status : Live in production Submitted : Jun 2026

Most companies do not fail at generative AI because the technology cannot do it. They fail because nobody can say which process deserves an assistant, and nobody agreed beforehand what success would look like. So the thesis produced two things: a method for picking the process, and a method for proving whether it worked.

What I did

  • Change-request classification and incident pattern recognition, both live in production
  • 14 use cases evaluated. 2 shipped. Impact measured.

How I found the candidates

The first attempt was interviews and a questionnaire. Two and a half weeks, 75 percent drop-out, and one pattern that explains it: the departments with the highest automation need have the least time to answer questions about it. Experts also tend to propose the process that annoys them rather than the one with the most leverage.

So I put the selection framework into a Rovo agent as its system prompt and pointed it at the process wiki. It came back with candidates nobody had named in the interviews, because they looked too trivial or sat too deep in the documentation. That is the point where the matrix stopped being a paper exercise. It is machine-readable, and that is what makes it scale.

How I picked the process

Every process gets scored on three axes and the weighted result decides, not enthusiasm. Data readiness and process fit count 40 percent each, organisational feasibility 20. The reasoning is that a motivated department cannot compensate for missing data, but bad data will sink a project no matter how motivated everyone is.

  • Data. Where does the information actually live, on a scale from people's heads and paper up to a database or an API. Plus whether it is current and consistent, whether there is history to learn from, and whether a technical interface exists at all.
  • Process. How often it runs, how stable the rules are, and what decisions are based on: gut feeling, experience, or recognisable patterns. Plus how much of the work is actually text.
  • Organisation. Are the people who own the process available in the project window, and how much value do they themselves expect from it.

One criterion runs the other way on purpose. If a process follows purely rule-based logic, its score goes down, not up. That case does not need a language model, it needs an if statement, and scoring it as a win is how organisations end up putting AI on problems that a rule already solves.

What the agents check

The change assistant verifies seven mandatory fields on the ticket, the link to the correct configuration item in the CMDB, and whether the approver selection follows the rules. The problem assistant searches for similar incidents and scores them: three to five is a possible cluster, more than five or three within four weeks is a confirmed one, and at that point it recommends opening a problem ticket. The pile it works on is 37,795 historical incidents with around 451 new ones a month.

How I measured it

  • A before and after evaluation on time, cost and quality, extended with established acceptance instruments so that user judgement is a number and not an anecdote.
  • The incident assistant cut analysis from roughly 30 to 60 minutes down to about two.
  • The change assistant did the opposite. It cost time and paid it back in field completeness, which turned out to be the metric that mattered in that process.
  • A sample of 15 fully traced incidents: 60 percent reached the right team straight away, 13 percent went through three or more reassignments, and around 80 percent were never linked to the similar cases that already existed.

What I took away

Two assistants, two opposite results, and a single success metric would have declared one of them a failure. Whichever number you pick up front decides what you are allowed to see afterwards. The other finding was less comfortable: what held adoption back was almost never technical feasibility, it was process readiness and the organisation around it.

← All projects

← All projects

Ongoing

Built with
Claude

Stack : Claude Code · Haiku, Sonnet, Opus by job State : Obsidian vault Since : Jul 2026, nightly

An agent loop that runs every night without me, plus the two commands I use every day to steer the ones that do not. The point is not that agents write code. It is that an agent which cannot be checked is worth nothing, so most of this is the checking.

The nightly loop

Five stages: a scout decides which workflows are due, the workers run, an inspector judges the output, a report card is written, and a set of checks runs over the result. The scout uses no model at all. It reads the schedule and whether the machine is online, and skips anything that needs the network when there is none, so an offline day does not poison the report card.

  • Every workflow carries a goal written in plain prose, including what explicitly does not count as failure. No new Strava activities is a success, not a miss. Without that line the inspector always finds something and you iterate against noise.
  • Models are picked per job rather than per taste: Haiku for research calls inside the workers, Sonnet for the work, Opus for the judgement. The expensive model only ever sees the verdict.
  • Vault maintenance and the Strava sync run daily, the job scan too, a discovery pass on Sundays. The deep evaluation only runs when I click it, because that one is worth paying for and should not fire at three in the morning.

Checks that do not trust the loop

Six checks run outside the agents and answer to nobody: does today's log exist, do files touched in the last day have valid frontmatter, has the loop itself completed in the last 26 hours, are there Syncthing conflict files sitting in the vault. They are plain shell, no model involved. An agent reporting its own success is not evidence, and the heartbeat check is the one that catches the failure mode everything else misses, which is the loop silently not running.

Two commands I run myself

  • /orchestrate turns a request into a goal, three to six falsifiable acceptance criteria each with a way to check it, and an explicit list of what is not a failure. That brief goes to a file, so it survives a context compaction. The artefact itself is allowed into my context exactly once, at acceptance. It also routes: for small or taste-driven work it sends itself back, because running the full loop to prove diligence is theatre.
  • /handoff writes the honest state of a session to a file so a fresh one continues without me re-explaining anything: what is done, the next concrete step, which files to read first, and which approaches were already tried and rejected. This page was built across several of those.

← All projects

← All projects

Computer Vision

Pose-Coach

Stack : MediaPipe Pose · FastAPI + React PWA Status : In progress
Analysis report showing knee, hip, elbow and ankle angles against their target ranges, plus a saddle height recommendation

A professional bike fit costs a few hundred euros and the systems behind it cost thirty thousand. I wanted to know how far you get with a phone camera and open source pose estimation. Upload a video of yourself riding and the app measures your position against bike fit target ranges.

How it works

  • MediaPipe Pose reads 33 joint keypoints per frame. No object detection, just the skeleton of one person.
  • Ankle height over time gives the top and bottom dead centre of the pedal stroke. At those points it measures knee, hip, elbow and torso angles and averages them across several crank revolutions.
  • Out comes a traffic light per angle plus a saddle height recommendation in centimetres, derived from leg measurements and knee angle, verified against a real video.
  • Videos never go to a cloud. Processing runs on my own Mac mini, the phone reaches it over Tailscale.

Each angle gets its target range, the spread across crank revolutions and the measurement uncertainty. Where the uncertainty is larger than the deviation, the app says so instead of giving advice.

What I took away

MediaPipe hands you 3D world coordinates, which look like the obvious thing to use. They are not. I measured two real 4K phone videos across all three model sizes: in 3D the knee angle moved by 19 degrees depending only on which model I picked, while the plain 2D pixel angles stayed within 2 degrees. The z axis carries the error, and 3D angles use all three. For a target window 10 degrees wide, the model choice alone would have decided the diagnosis.

← All projects

← All projects

Demoed live

Forge
Apps

Stack : Atlassian Forge · Jira · @forge/kvs Built for : Two ACE talks

A customer with two sites, three pool cars and no software beyond Jira and Confluence. The cars were managed by shouting across the office, by email, and by a shared Outlook calendar that was never right. You walk to the car park, the car is gone, a colleague is three cities away with it.

Confluence documents the problem but cannot book anything. Plain Jira takes the request but validates nothing, so three people book the same car for the same Tuesday. Forge sits on the infrastructure that is already there and puts the logic on top.

Pool car booking

  • The fleet lives in Forge storage, managed from an admin page. Name, site, model, plate, status. No database server anywhere.
  • Booking happens in an issue panel, so nobody has to leave the ticket they are already in. The booking is tied to the user and the issue key.
  • A resolver validates asynchronously before anything is written: is the car free for that window, and is it at the right site. On a conflict the panel says which colleague has it and for which ticket, instead of just refusing.

This is the app I built live on stage at ACE Dornbirn and again at ACE Leipzig. No slides, no mock, no backup. Empty folder to deployed Jira app in front of the room.

CRM

  • Opportunities, activities, pipeline and forecast, with a dashboard gadget on top.
  • Storage uses one key per entity with a prefix rather than a single flat array, which is what makes querying a pipeline stage possible at all.
  • The resolvers are split by domain and registered from one index file. With this many endpoints a single resolver file stops being readable.

What I took away

The deprecated paths cost me the most time. @forge/ui and the old storage API from @forge/api are out, and half the examples online still use them. HTML tags do not render in UI Kit at all, and the Table component people reach for does not exist, it is DynamicTable. Running forge lint before every deploy caught the missing scopes that otherwise only surface as a runtime error in front of an audience.

← All projects

← All projects

Ongoing

Cloud
Migration
Assessment

Stack : Next.js · Ollama · Marketplace API Mode : Local-first tool

Before a company moves its Jira from Data Center to Cloud, somebody has to work out what will break. That answer usually comes from a consultant reading through an export by hand and estimating from experience. The inputs are always the same, so I built the tool that reads them.

You drop in the support zip that Jira Data Center exports, optionally a ScriptRunner export alongside it, and get back four things: which apps are installed and which of them have a Cloud counterpart, how complex the custom scripts are and which of them use APIs that Cloud does not have, a risk assessment, and an effort estimate broken into phases from discovery to cutover.

Why it can run without sending anything anywhere

A support zip is a customer's production system in a file. Whether that may be sent to an AI provider is not the tool's call to make, so the provider is a setting. It defaults to a local model through Ollama, and in that mode the only request that ever leaves the machine goes to the Atlassian Marketplace with an add-on key in it, nothing else. Where a hosted model is allowed and better output is worth it, the same analysis runs against Claude instead. On top of that sits a metadata-only mode that leaves script source out of the request entirely.

How the model gets its context

  • A layer of hard migration rules, kept as files rather than in the prompt: API and automation limits, data residency, what ScriptRunner does and does not survive, which third party apps shift time zones or keep their data outside Jira.
  • A layer of learnings from assessments already done, so the same surprise does not have to be discovered twice.
  • The instance itself, parsed out of the zip.

Separating those three matters more than the model choice. The rules are the part that must not drift, and keeping them in files means they can be corrected without touching the application.

← All projects

← All projects

In daily use

Strava
Sync

Stack : Python · Strava API · Obsidian State : 394 activities, three years
Generated sport dashboard with totals, a fitness and fatigue curve, and monthly training volume by sport

Strava knows what I did. My notes know what I planned, how I slept and how hard it felt. Those two never talked to each other, so I wrote the bridge.

How it works

  • OAuth against the Strava API once, then a sync command pulls everything new and writes each activity as its own markdown file into my vault.
  • Activities land in a monthly file and in that day's log, so a training session sits next to the notes I wrote about it instead of in a separate app.
  • A second script reads those markdown files back and generates the dashboard. The vault is the source of truth, not the API.

394 activities over three years. The curve is a fitness and fatigue model derived from duration and sport type, which is a proxy: without a power meter there is no real training load, so it weights sports against each other instead.

What I took away

The interesting part was not the API, it was deciding what counts as load. Without a power meter there is no measured intensity, so an hour of swimming and an hour of walking would look identical. Weighting each sport against the others is a crude fix and it is visibly wrong at the edges, but it is the difference between a chart that tracks how I actually feel and one that just counts hours.

← All projects

© 2026 Kilian Warmdt Legal notice Privacy