moto-verdict
A motorcycle buying guide for French-speaking riders. Today it is a static editorial site, a git-backed CMS and a content pipeline that drafts articles from news feeds and checks every number before a human reads them. The guided funnel the product is built around is not written yet.
Private repository
01What it is
The goal is to help a rider find the motorcycle that fits them, by crossing licence class, morphology, real usage and taste, and to return a short ranked list with the reasons behind it. Articles bring readers in. The funnel and its recommendation API are specified but not built. What exists is the editorial side, and it is the part I worked on first.
02What I built
A public site and an admin panel, both React and Vite on Cloudflare Pages, sharing one design system whose tokens I extracted from the Figma file. A content pipeline in TypeScript that reads feeds, drops duplicates, classifies items, assembles a brief, drafts the article section by section and opens a pull request for an editor. Decap CMS sits on that pull request so an editor can review, ask for a rewrite or approve. The infrastructure is declared in the repository: workflows, systemd units, DNS and the host bootstrap scripts.
03How it runs
Nothing reaches the site without a human merge, and that merge is the only trigger for a deploy. The site is built from the articles in the repository, so a reader is served static pages and a VPS outage does not take it down. The pipeline runs on a VPS under systemd timers, with its state in PostgreSQL. The model is called through the Codex CLI as a locked-down subprocess: read-only sandbox, no user config, no web search while it writes. Changes follow a feature, dev, main flow that a CI job enforces.
04What I learned
The model never chooses a motorcycle and never supplies a number. Figures are extracted by code into a whitelist before drafting, and a deterministic check that understands French number formatting rejects any figure outside it. I also measured that title similarity cannot separate a duplicate from a different story about the same bike, so I set the duplicate threshold high and accept that a few duplicates reach the editor. A missed duplicate costs one click. A wrong merge silently loses a story.
How it is built
- A reader opens the site
- An approved article goes live
- A feed item becomes a draft pull request
- Pipeline state in the database
- Classification and drafting calls
People
Who uses it
Cloudflare
Static hosting
GitHub
Source of truth
VPS
Content pipeline
External inputs
News feeds and the language model
Select a part of the diagram to see what it does. Each color is one flow through the system.