How I use AI / a tour

Claude Code is my workhorse.
I build, run, and train with it.

I use it to develop apps, troubleshoot problems, and run my homelab infrastructure as code — writing and testing Ansible playbooks used to be a slog, now Claude does it better and faster than I did by hand. This is a tour of how, from infrastructure through to the deepest build: an AI investment analyst.

1,136
commits, 3 systems
4
servers, zero hand-written playbooks
1
subscription cancelled
6,569
lines, flagship app in 11 days
BUILD · commits per system ▲ 1,136 total
Real data: git log across three repos. Same tool, three very different jobs.
SCROLL ▼

01 / Infrastructure as code

I stopped hand-writing Ansible playbooks

My homelab spans an Oracle Cloud VPS, an HP EliteDesk G4 server, two Raspberry Pi 5s, and a scatter of ESP32 sensors. The four Linux hosts are managed as one Ansible repo, deployed automatically by self-hosted Gitea Actions on every push. I used to write the YAML myself, then spin up a spare host to find out what I'd gotten wrong. Now I describe the change and Claude writes the role, the task, the template — and reasons about idempotency and host-scoping better than I bothered to when I was doing it by hand.

◆ ROLES

bootstrap · docker · storage · watchdog · tailscale

Eight reusable roles apply consistently across all four hosts — new host, same playbook, site.yml --limit <host>.

▲ CI/CD

Push to main, fleet updates itself

Gitea Actions triggers a runner per host on every push — no manual ansible-playbook from my laptop, ever.

◇ PRIVATE

Tailscale, on a self-hosted coordinator

Headscale — my own Tailscale control plane, running on xenon — meshes all four hosts and gates everything that isn't meant to be public.

◆ PUBLIC

Cloudflare in front, Traefik behind

Public services either route through a Cloudflare Tunnel (no open ports) or straight through Traefik with Let's Encrypt certs issued via Cloudflare's DNS-01 API.

1

Describe the change PLAIN ENGLISH

"Add a watchdog role that restarts a container if its healthcheck fails three times" — no YAML written by me at this stage.

2

Claude writes the role CLAUDE CODE

Tasks, handlers, templated compose fragments, and the vars.yml wiring — following the conventions already in the repo, not inventing new ones.

3

I review, then push GIT

I read the diff like any PR. If it's right, it goes to main — that's the only deploy step.

4

Gitea Actions converges the fleet CI/CD

One runner per host applies site.yml --limit <host> in parallel. Idempotent by design — safe to re-run, safe to fail halfway.

4 hosts · one ansible repo
d5Raspberry Pi 5 · smart-home hub
m1HP EliteDesk · general-purpose server
xenonOracle Cloud VPS · Gitea, VPN, public services
ts1Raspberry Pi 5 · Tailscale relay / edge
833 commitsJan–Jul 2026 · 94 active days · 8 roles
plus the edge — not Ansible's job
ESP32 microcontrollersESPHome, not Ansible · sensors into Home Assistant
Why trust AI-written infrastructure code?
Same discipline as application code — I read every diff before it merges. What changed is the tedious part: writing boilerplate YAML and manually testing it against a spare host is now instant, not a discipline I had to skip under time pressure.
Why still Ansible, not shell scripts?
Idempotent, host-scoped runs mean Claude can safely re-run site.yml without me worrying about drift — the exact property that makes AI-generated infra code safe to trust.
What changed day to day?
Adding a service used to mean writing a compose template, a secrets template, and vars wiring by hand. Now it's one conversation, and the tested, idempotent result ships on the next push.

02 / How I build apps

I describe. Claude codes. I review. CI ships.

Vibe coding — as Dennis Denuto would say, it's not strictly speaking a methodology, it's more... the vibe of the thing.

No IDE marathon sessions. Development is a conversation: I say what I want in plain English, Claude Code reads the codebase, writes the change, and explains it. My job shifts from typing to deciding — and the same loop runs whether I'm shipping a feature, fixing a bug, or standing up a whole new app.

Describe intent

"Add a self-healing model picker to the coach" — or whatever the app of the day needs.

Claude implements

Model, migration, API route, UI — in one pass, following existing patterns in the repo.

I review & steer

Read the diff like a code review. Push back, refine, catch what the AI missed.

Push to main

Gitea Actions builds the images and deploys. I never run docker build by hand.

Watch it break

Real usage finds the edge cases. Each one becomes the next conversation.

  the same loop, across three unrelated codebases
6b181f8 Add dual-model selection layer for fast vs strong AI tasks     invest
a91c2e4 Add self-healing model picker with daily validation           training
f30d117 Add watchdog role: restart containers on failed healthcheck      infra
ab75482 Fix price forecast: handle empty LLM response and reframe prompt invest
c48e910 Replace Python best-picking with a SQL window function                training
7d2b6aa Fix idempotency gap in tailscale role on re-run                   infra

Different domains, same rhythm: add → fix → refine → harden. Small conversational increments, each one shippable. Context lives in CLAUDE.md and Claude's persistent memory, so every session starts already knowing the project — whether that project is a trading dashboard, a training log, or a fleet of home servers.


03 / When a subscription killed my training app

So I built my own. It's free, and better.

The app I'd used for years to plan marathon training quietly stopped working, and the fix the owner shipped was a subscription. Instead of paying for a worse version of what I already had, I asked Claude Code to help me build PMCDash: it syncs 18 years of Garmin history, models training load the way real coaches do — fitness, fatigue, form — and adds an AI coach that reads my actual data. It runs on my own server for nothing.

254
commits, 58 days
18
years of training data synced
676
activities, 6,534 km
$0
monthly cost, self-hosted
pmc.insightiq.au/pmc
Performance Management Chart: daily training load bars with fitness, fatigue and form lines
The core view. Daily load, accumulated fitness, and form — the same physiology every training plan is built on.
/coach
AI coach chat interface grounded in the user's actual training data
The AI coach. Answers grounded in my real training history via tool calls, not generic advice.

I won't go deep on this one — the pattern is the same as everywhere else. What's worth noting: it isn't a lesser replacement. It's tailored to exactly what I want to see, with none of the features I never used and none of the recurring bill.


04 / The deep dive

Now, the one I'll actually walk you through

INVEST is an analyst that never sleeps: a full-stack monitoring app for my ASX and US holdings, running on an Oracle Cloud VPS alongside my other public services, priced live, assessed daily by an LLM, and advised weekly. It's the build where every architectural decision mattered enough to explain — so this is where the tour slows down.

▲ WATCHLIST

Live multi-exchange quotes

ASX and US tickers side by side, with FX conversion to AUD, 52-week ranges, and price alerts pushed to my phone via ntfy.

◆ ASSESSMENTS

Daily AI verdicts

Each stock gets a Bullish / Neutral / Bearish call every day, grounded in that day's news — and it remembers its last five assessments for continuity.

◇ THEMES

Monitoring briefs

When I add a ticker, the AI writes five investment themes for it, then turns them into news search queries it tracks every day.

▼ FORECASTS

Scored predictions

The AI forecasts price ranges — and the app checks them against reality when they expire. The model is held accountable.

invest.insightiq.au
Dashboard: watchlist of nine ASX and US tickers with live prices, day change, volume and 52-week range
The dashboard. Live watchlist across exchanges — USD positions converted to AUD, advancing / declining at a glance.
/stock/WBC
Stock detail page for WBC: price chart and the AI-written monitoring brief with five investment themes
Stock detail. Price history plus the AI-written monitoring brief — five themes it watches for this stock every day.
/trends
Trend analysis page: per-stock cards with 7, 30 and 90 day sentiment trends and price forecasts
Trends. Rolling 7/30/90-day sentiment per stock, with the AI's price forecasts and their scored outcomes.

05 / The loop, for real this time

The same rhythm from section 02 — here's the actual log

This is what "describe → implement → review → push" looked like for INVEST specifically: 49 commits in 11 days, each one a small, shippable increment.

  git log --oneline · invest, Jun 27 – Jul 7
3d79fca Add AI price range forecasts with outcome tracking
ab75482 Fix price forecast: handle empty LLM response and reframe prompt
0e5e01d Use fast model for price forecasts                    ← refine
4407c03 Add cash positions with interest rate tracking
352c7b1 Include cash positions in AI portfolio advice          ← extend
eba8eb8 Fix N+1 queries in portfolio summary and advisor      ← harden
6b181f8 Add dual-model selection layer for fast vs strong AI tasks

6,569 lines of code, 49 commits, 11 days. Context lives in CLAUDE.md and Claude's persistent memory, so every session starts already knowing the project.


06 / Architecture

Decisions were mine. Research was free.

AI didn't decide the architecture — it made every option cheap to evaluate. Each choice below started as a question to Claude, came back with trade-offs, and ended with me picking.

docker compose · 4 services
frontend :3000React 18 · TypeScript · Vite · Tailwind · nginx proxies /api → backend
backend :8000FastAPI · async SQLAlchemy · APScheduler · the AI layer lives here
postgrespositions · assessments · trends · forecasts
redisnews cache · FX cache · advice cache (6h TTL)
Yahoo Financequotes via curl_cffi
TogetherAItwo LLMs, auto-selected
the platform it actually runs on
Traefikreverse proxy · auto Let's Encrypt · invest.insightiq.au
Gitea Actionsbuilds & pushes both images on push to main
restic + BackRestnightly snapshot of postgres & redis volumes to the NAS, 7 daily / 4 weekly / 6 monthly retention, healthchecks.io ping on success or failure
Why not the yfinance library?
It kept getting rate-limited. Claude proposed hitting Yahoo's chart API directly with curl_cffi (browser-impersonating TLS) — faster and reliable.
Why async everything?
One scheduler process fetches prices, news, and AI calls concurrently. asyncpg + httpx keep a 5-minute refresh cheap on a small cloud VPS, not a beefy homelab box.
Why a scheduler, not cron?
APScheduler lives inside the app: jobs are timezone-aware (4:30pm AEST close), reconfigurable from the Settings page, no extra container.
Why Redis in a one-user app?
News and LLM output are expensive to regenerate. Caching advice for 6 hours means the dashboard is instant and the API bill stays near zero.
Is invest its own deployment?
No — it's four docker-compose services templated into the same Ansible repo from section 01. It rides on the reverse proxy, the CI runner, and the nightly backup job every other service on the fleet already has. Nothing about "run an app" was built twice.

07 / The model decision layer

Not every task deserves the expensive model

The app makes hundreds of LLM calls a week. Reformatting a news query doesn't need the same brain as judging my portfolio — so I built a routing layer: every AI task declares whether it needs strong judgment or just fast transformation. The result: the entire AI layer runs on ~$2 a month.

strong — judgment, synthesis, advice fast — extraction, queries, summaries

The models pick themselves

At startup the backend pulls TogetherAI's live model catalogue and auto-selects both tiers by price as a proxy for capability: the most expensive chat model becomes strong, the cheapest becomes fast. Both are overridable in Settings — a sensible default, not a cage. When better models ship, the app upgrades itself on next restart.

  analysis_service.py
async def _chat(messages, max_tokens=1024, fast=False):
    model = (settings.TOGETHER_MODEL_FAST
             if fast and settings.TOGETHER_MODEL_FAST
             else settings.TOGETHER_MODEL)
    ...

# startup: pick both tiers from the live list
strong = max(priced, key=lambda m: m["output_price"])
fast   = min(priced, key=lambda m: m["output_price"])

08 / The monitoring themes

Every stock gets 5 signals worth watching

The moment I add a ticker, the two model tiers go to work together. The strong model thinks like a long-term shareholder and writes five key themes — earnings drivers, growth bets, macro sensitivities, capital allocation. The fast model then compresses each theme into a Google News query phrased the way journalists actually write headlines.

◆ Themes — strong model, on ticker add
▲ Search tasks — fast model, 4–7 words
Net interest margin trendThe core earnings driver for a bank — rate moves hit it directly.
Westpac net interest margin outlook
Housing & mortgage growthLoan book growth sets the revenue ceiling.
Australian mortgage lending growth
Credit quality & impairmentsBad debts are the downside scenario to watch.
Westpac bad debts provisions
Capital managementDividends and buybacks signal board confidence.
Westpac dividend buyback announcement
Cost-out & digital transformationThe efficiency story underpinning the margin thesis.
Westpac cost cutting technology

Illustrative pair for Westpac (WBC). The query prompt carries hard rules: anchor to the instrument's base search term, ban abstract finance jargon like "valuation multiples", and no two queries may retrieve the same articles — each targets a distinct signal.

1

News cron, every day SCHEDULER

All five theme queries run against the news sources daily, alongside the stock's general query. Results land in Redis, tagged per theme.

2

Themes feed the daily verdict STRONG MODEL

The 4:30pm assessment prompt includes a "theme-specific news today" section. The model must comment on each of the five themes before it's allowed a Bullish / Neutral / Bearish call — no verdicts from vibes.

3

Verdicts become trend lines HISTORY

Each verdict is scored (+1 / 0 / −1) and stored. Rolling 7, 30 and 90-day windows compare first-half vs second-half averages to call the direction: improving, stable, or deteriorating.

4

Trend narratives FAST MODEL

For each window, the fast model writes a short narrative of what moved sentiment — the story behind the line on the Trends page.

This is the feature that makes the trend assessment mean something: the AI isn't reacting to generic headlines — it's tracking the five signals it decided matter for this specific stock, every single day.


09 / AI on my portfolio

The LLM never guesses — it reads my numbers

The portfolio advisor isn't "ask ChatGPT about stocks." The app computes every fact deterministically — weights, P&L, sector concentration, cash yield — and hands the model a structured brief. Code does the arithmetic; the model does the judgment.

1

Load positions SQL

Stocks and cash accounts, batch-loaded with their latest sentiment and 7-day trend — three queries, no N+1.

2

Enrich deterministically PYTHON

Portfolio weights, unrealised P&L, sector and exchange allocation, cash percentage, projected interest income — all computed, never asked of the model.

3

One structured prompt STRONG MODEL

The full brief plus a fixed contract: reply in exactly five sections, grounded only in the numbers given, no legal boilerplate. Prompt design is API design.

4

Cache & serve REDIS · 6H

Advice is cached for six hours; empty responses are detected and never cached. The dashboard reads instantly.

5

Close the loop FAST MODEL

Price forecasts get resolved against actual outcomes when they expire, and daily verdicts roll up into 7/30/90-day trend lines. The AI's track record is data too.

◆ Advisor output — the fixed five-section contract
  1. Portfolio strengthsWhat's genuinely working, in the data.
  2. Concentration & risk flagsOverweight positions, correlated sectors.
  3. Diversification gapsWhat the allocation is missing.
  4. Specific improvementsConcrete, actionable suggestions.
  5. Overall assessmentThe honest bottom line.
/portfolio
Portfolio page: positions with weights and P&L, cash accounts, and the Portfolio Advisor panel (amounts pixelated)
The portfolio page. Positions, cash accounts with interest tracking, and the one-click Portfolio Advisor. Amounts pixelated — the AI sees them, the audience doesn't.

Feeding it back: every day's Bullish / Neutral / Bearish verdict is stored as history, and the next assessment reads its own last five — the analyst has memory and accountability, the two things that make an opinion worth reading.


10 / Lessons

What actually transfers

Infrastructure, apps, a training log, an investment analyst — different problems, same handful of lessons carried across all of them.

◆ 01

You are the decision layer

AI writes code — and playbooks, and prompts — faster than you can read them. The scarce skill moves up a level: framing the problem, choosing between trade-offs, and knowing when the answer is wrong.

◆ 02

Context is the multiplier

CLAUDE.md, persistent memory, and small shippable commits mean every session starts warm. The better the AI knows your project, the less you repeat yourself — true for a trading app and equally true for four Ansible-managed servers.

◆ 03

Route models like you route work

Strong model for judgment, cheap model for transformation — the same way you wouldn't send a formatting task to your most senior engineer. It's one boolean in my code — and it keeps a daily-assessment AI pipeline at about $2 a month.

◆ 04

Make the AI show its work — and score it

Structured prompts, computed inputs, cached outputs, and forecasts graded against reality. Trust in AI output is built the same way as trust in people: track record.

◆ 05

Engineering judgment is still the difference

Anyone can vibe-code a working app now — and it would genuinely work. But "working" hides the downsides: N+1 queries that crawl as data grows, no caching so every page load burns API calls, blocking calls that freeze the scheduler, ad-hoc schema changes that eventually eat your data, Ansible roles that aren't actually idempotent. I caught and fixed every one of those across these builds because I knew to look for them. AI removes the typing; it doesn't remove the need to know what good architecture looks like — it makes that knowledge more valuable, because it's now the only differentiator.