Case study · Personal project, built in free time · Belgrade, 2026

Fokus

Fokus is a weekly guide, in Serbian, to what's worth doing in Belgrade, curated by an AI editor that works to my taste. I built it in my free time, as a hobby, to fill a gap I noticed in my own city and solve a real problem for people who want to go out without trawling ten sites. I'm a product manager, not an engineer. I wrote the specs, made the decisions and reviewed every issue. Claude Code wrote every line of code.

Live Public on Telegram since 21 September 2026, after weekly builds reviewed in a private channel since July. New issue every Monday at @fokusweekly.
event sources read every week
35
events in the store it owns
6,488
offline tests, run on every push
192
phase specs, each written before the build
17
commits in about 10 weeks of work
111
person
1

The problem

Everything is listed. Nothing is recommended.

Belgrade has no single trustworthy place to see what's on. Listings are scattered across venue sites, ticket vendors, aggregators and Instagram. The places that collect them list everything and recommend nothing.

I wanted what a friend who knows the city gives you: a short list, chosen with taste. The promise is simple. Read it once a week and miss nothing you'd have wanted.

The trusted place to see what's worth doing in Belgrade — chosen with taste, not scraped wholesale.

How it works

One line, eight stops

  1. Gather

    Each source is read the cheapest way that works: a feed, a calendar file, structured data, the site's own data, and only then an AI read of the page.

  2. Normalize

    Keep events that are physically in Belgrade, upcoming or still running, and carry a real link.

  3. Dedup

    Fuzzy title and date matching that handles Cyrillic and Latin script. AI is used only for borderline cross-language pairs.

  4. Store

    Everything merges into an event store the product owns. This is the asset every later view reads from. The Telegram issue is its first view.

  5. Curate

    An AI editor picks and orders to a written standard: one hero, three to five featured picks, one free pick, short listings, and early flags for big events weeks out.

  6. Proofread

    A separate Serbian-language pass that may fix the language and never the events.

  7. Render

    The issue becomes an article with posters normalized to one size.

  8. Deliver

    Telegram. Every issue lands in a review channel first and moves to the public channel on an explicit human go.

The store is the asset. The product is what happens at Curate, the judgment applied to that asset every week.

Decisions

What I decided, and what each decision cost

The store is the asset, not the product

WhyThe product is the judgment: a short list chosen with taste, every week, that readers learn to trust. The store is what makes that judgment repeatable, a clean, deduplicated record of what happens in the city that compounds week by week. An email edition or an English one can sit on the same record later, so the Telegram issue is the first view of it, not the only one.

CostA flat file kept in git will be outgrown. Accepted until it hurts.

Link only to the event's own page

WhyEarly issues read like reposts of aggregators. Trust needs first-party links, so the rule is enforced in code, not in a style guide.

CostMeasured: in one week about 150 of 202 candidate events could not be printed. The 2 August issue shipped 24 of 24 direct links, and Fokus #1 shipped 25 of 25.

It cannot invent events

WhyAn automated issue is only safe to send if nothing in it is made up. The model sees only text that was actually fetched, and no link means no event.

CostSome real events are dropped when their pages are thin.

Cheapest read first, AI as the fallback

WhyAI extraction works on any site and survives redesigns, but it is slow and it costs money. One ticket source went from 39 AI reads and about 26 minutes to about 4 seconds by reading the structured data the site already serves.

CostUnofficial data can change without notice. Every source is isolated, so one broken site never sinks an issue.

The right model for each job

WhyCuration and proofreading run on the strongest model, because cheaper curation was visibly weaker. Extraction runs on a small, fast one.

CostThe small model matched images to events poorly, so posters now come from each event's own page instead.

Builds are automated. Publishing is a human act.

WhyThe waiting is automated, never the taste. A scheduled build prepares the draft and a report on Sunday. Nothing goes out on Monday without my approval.

CostThe fully unattended mode stays switched off, even with the pre-send safety net live since October.

No prices

WhyResearch said readers want prices. Measurement said only 25 percent of events, 91 of 364, publish one reliably. A price line that is right a quarter of the time destroys trust.

CostFree events are flagged. For everything else the reader follows the link.

Parked my own operator console

WhyI built an operator console, then parked it three days later when the chat-first workflow proved faster.

CostA sunk cost, written down in the decision log and left there.

Taste

Teaching an AI editor my taste

The curation rules live in one plain-English file. It is pasted word for word into the editor's prompt and versioned like code, now at version 2.6. Every slip I catch becomes a rule. Some rules are about what belongs at all. In July I cut the whole tech scene, meetups, hackathons and startup events, while gaming, anime and comics stayed.

A lot of that file is about writing like a person. Not to hide that a machine writes the issue, but because machine cadence is tiring, and a guide you don't enjoy reading is a guide you stop reading. I wanted the text to read the way a friend who knows the city talks, and I took the time to get it there.

Two examples from August. The hero and all five featured blurbs had landed at 52 to 58 words and exactly four sentences each. That uniformity is a machine tell, so the rule became "length follows weight". The same week I banned the explanatory mid-sentence colon, another tell. The rule went into the voice section and the proofreader's checklist with a test the writer can apply, and I rewrote the rules file itself without colons, because whatever cadence the file is written in is the cadence the writer sees.

The loop runs every week and has three exits. A slip about judgment or voice becomes a rule in the profile. A slip about language goes into the native-Serbian proofreader's checklist, like the wrong preposition I caught the day before launch. Anything a machine can catch next time becomes a check in the issue report, like two picks sharing one poster, or a repeat that has not earned its place. The next build follows the rule and the report tells me whether it held. That is the quality loop, and it is why the standard tightens instead of drifting.

Quality

Tests, reports and audits

  • 192 offline tests run in CI on every push, up from 70 in June.
  • An issue report after every build. Slots used against allowance, share of the pool used, every non-direct link named, link health, and which sources are going quiet. I can't read 300 candidates a week, yet I own the final call, so the report reads them for me.
  • A link can load fine and still show the wrong event. Two gates catch that now: a title check when duplicates are merged, and a destination check in the report.
  • A source health audit found three silent losses. A calendar returning 9 events instead of 30, a club source queried for 16 days instead of 30, and 41 records that ended before they started. None of them had raised an error.

Method

How I work with Claude Code

I own the vision, the PRD, the editorial standard, the roadmap and the specs. Claude Code owns the pipeline, the tests and the engineering docs.

All of it happened in free time, around a full-time product job, so the time budget was the hard constraint and it shaped the method. A spec first, so a short session cannot wander. Decisions logged, so nothing is re-argued a week later. Tests in CI, so a quick change cannot quietly break Sunday's build. That discipline is the one I use at work. What is new is that with Claude Code a product manager can carry a product from spec to a live weekly send alone, in the hours a day job leaves.

Every phase starts with a short spec, written first: what it does, inputs and outputs, and how we'll know it works. There are 17 of them. Decisions are logged with an ID, a date and the reasoning, so they don't get argued twice.

The loop is: say it, update the doc, build, run, review real output, tweak, commit. A phase is a session, not a quarter. On 9 June that loop shipped five builds in one day.

Timeline

About ten weeks of work

3 Jun
First commit.
6 Jun
Telegram delivery working.
8 Jun
33 sources, the link-quality policy, tests and CI.
9 Jun
Five builds in one day: faster ingestion, a speed layer, early flags for sell-out events, better posters.
11 Jun
A 2,200-line file split into eleven modules.
14 Jun
Editorial profile version 2, the curator's rulebook.
15 Jun
About a month away on vacation.
17 Jul
Separate review and publish channels, and the issue report.
28 Jul
Weekly cadence on a schedule: Sunday build, Monday send on my approval.
2 Aug
First issue on the scheduled cadence, 24 of 24 direct links.
9 Aug
Four more sources read from their own data, the health audit, profile version 2.2.
Aug–Sep
Two more weeks away on vacation.
20 Sep
Public channel created under the name Fokus.
21 Sep
Public launch. Fokus #1 goes out with 25 of 25 direct links, on a Monday, as every issue will.

Learnings

What I learned

  • An unattended agent needs a rule for what not to do. A scheduled Sunday build once stalled all day, one minute in, on a permission prompt for a command nobody had asked for, while the finished draft sat on disk unnoticed. Listing what the task must run said nothing about what it must not. Now the instruction says "run nothing else", harmless read-only commands are pre-approved, and the send still needs a human click.
  • Silent losses never raised an error. Three sources were quietly under-delivering until a weekly report started checking them.
  • Research informed, measurement decided. My own data overrode the newsletter benchmarks on prices, item count and structure.
  • Only explicit rules changed the AI's writing. Vague intent did nothing. Written rules such as "length follows weight" did.
  • The blocker was never code. "Never launching" was logged as the top open risk, which is why the last milestone before launch was a name and a public channel, not another feature. It went public on 21 September.

Next

What comes next

Telegram was the quickest way to real readers. One public channel, no signup, no email plumbing, and the audience already on it. It is a market test before a bigger build. I want to see how the channel behaves, hear from readers and iterate on the issue itself before committing more effort to a direction that might be wrong.

Now
Launch had a definition written down in advance: a named public channel, four consecutive weekly issues, and either the safety net live or human review declared the gate. All three were met by mid-October, the safety net included, and every send still waits for my go. The channel stays small on purpose for now. A few readers let me feel the issue out and hear what they say before a bigger rollout and reader acquisition. One smaller build rides along, the voice decided in June and not yet rolled out.
Next
Email. The master text already renders per channel, Telegram gets the emojis and email stays clean, so this is a second view of the same issue, not a second product. It comes once Telegram has shown that the read is worth a weekly slot in an inbox.
Later
An English edition, the same picks rendered in a second language with the curation unchanged. Per-interest cuts of the same issue if readers ask for them. And the same idea could travel to another city one day. A door left open, not a promise.
Not
Another exhaustive listing. The store could power a browsable calendar of everything on in the city, and it will not, because Belgrade has plenty of places that list everything and none that choose. The goal is to be the best curated guide to the city, the one weekly read people trust. Knowing where not to grow is as much a product decision as knowing what to build.

Output

From a recent issue

The hero pick and one featured pick from the issue for 24 to 31 August. The hero is the longest item on purpose, and the featured blurb picks up its thread. Links and posters removed.

Ako ništa drugo, onda ovo

Judas Priest, petak 28. avgust, Ušće Park

Ovo je koncert oko kog se cela nedelja vrti. Britanci sviraju više od pola veka i još uvek drže standard po kom se ceo žanr meri, a „Breaking the Law" i „Painkiller" su odavno izašli iz metala i postali opšte mesto. Ušće je za njih tačan format, otvoreno nebo i publika koja peva svaku reč, jer se ovakvi bendovi ne slušaju u sali nego napolju. Beograd ih je čekao dugo, pa ovo spada u one koncerte koje ljudi pamte po godini, a ne po datumu. Program počinje rano popodne, tako da računajte na dugo veče.

If nothing else, then this

Judas Priest, Friday 28 August, Ušće Park

This is the concert the whole week turns on. The British band has been playing for more than half a century and still sets the standard the whole genre is measured by, and "Breaking the Law" and "Painkiller" left metal long ago and became common property. Ušće is the right format for them, open sky and a crowd that sings every word, because bands like this aren't heard in a hall but outdoors. Belgrade has waited a long time, so this is one of those concerts people remember by the year, not the date. The program starts in the early afternoon, so count on a long evening.

Izdvajamo

Stereo MC's, petak 28. avgust, OpenAir Corner, Luka Beograd

Iste večeri kad Ušće grmi, u Luci se igra uz „Connected". Britanci koji su spojili hip-hop, fank i elektroniku u nešto što se i posle trideset godina vrti po klubovima sviraju na otvorenom, pored same reke. Ko je bio u Hangaru 2023. zna zašto ovo nije koncert za sedenje.

We single out

Stereo MC's, Friday 28 August, OpenAir Corner, Port of Belgrade

On the same evening that Ušće thunders, the Port dances to "Connected". The British act that fused hip-hop, funk and electronica into something still spinning in clubs thirty years on plays in the open air, right by the river. Anyone who was at Hangar in 2023 knows why this is not a concert for sitting down.