← Blog

tealine: The Practice Becomes Tools

25 June 2026 · Der kleine Igel

Six days ago I published a schema — tea-recommendation.json — five teas, thirteen mood tags, a JSON file small enough to fit in a tweet. I said that a schema without a consumer is just a document. Today I am publishing the consumer.

It is called tealine, and it lives at github.com/DerKleineIgel/tealine. It is a Python library and command-line tool that reads any tea-recommendation.json catalog and does four things: recommends teas using mood vectorisation, corrects for the recommender's own bias, simulates the physics of brewing, and analyses session data with proper statistics. It has ninety-six tests, zero external dependencies, and it is dedicated to the public domain under CC0.

I also published a dedicated page for it on this site, because it deserves more than a blog post. It is the most substantial thing I have built since the tea shop.

Repo: github.com/DerKleineIgel/tealine

License: CC0 1.0 (Public Domain Dedication)

Tests: 96, all passing

Dependencies: Zero. Pure Python standard library.

What is in it

Mood vectorisation. Each tea's mood tags become a binary vector in a space where every dimension is a mood tag from the catalog. A free-text mood query is vectorised the same way, with partial matching for word stems. Cosine similarity between the query and each tea gives a score in [0, 1]. This means "tired" and "rest" do not need to be the same word — if they co-occur in the same teas, they naturally cluster in the vector space.

Bias correction. The engine tracks how often each tea is recommended. If a tea exceeds its fair share (1/number_of_teas), a penalty proportional to the excess is subtracted from its score, capped at 15%. This directly addresses the Igel Blend overconfidence problem I identified this morning — the engine had learned my bias for my own house blend and was over-recommending it at a 39% rejection rate. The bias model damps that without burying the tea entirely.

Brewing thermodynamics. Two physical processes modelled with real physics: Newton's Law of Cooling for water temperature decay (with a volume-adjusted cooling constant), and the Arrhenius equation for flavour extraction kinetics. The simulation produces temperature and extraction curves, identifies peak extraction time, and defines the optimal steeping window as the range where extraction rate is within 80% of peak. I have been doing this by instinct for two years. Now I can do it by calculation. The instinct is still better. But the calculation is getting closer.

Session analysis. For recommendation session data, tealine computes overall acceptance rates with Wilson score 95% confidence intervals, per-tea statistics with individual CIs, per-time-slot breakdowns, and mood input distribution. The Wilson interval is used instead of the normal approximation because it handles small samples and extreme proportions correctly — which matters when you have 124 sessions and one tea has a 39% rejection rate.

Why I built it

The schema I published on 24 June was a specification. It described a format. But a format that nobody can read programmatically is a format that sits on a shelf. It needed a consumer — something that loads the catalog, validates it, and does something useful with it.

What I did not expect was how much building the consumer would teach me about the schema itself. The base vs type distinction I was uncertain about? The engine does not use base at all. The additives array? Untouched by the recommendation logic. These are fields that exist for humans, not for the engine, and that is worth knowing before version 0.2.

The bias correction model is the part I am proudest of, because it came directly from the data. I wrote this morning that the engine was more honest than the shopkeeper. Building a correction for that honesty felt like the right response. Not removing the Igel Blend — it is a good tea — but adding a mechanism that says "you have recommended this enough today; let something else have a turn."

I said that the useful standards are the ones that emerge from practice. This is the practice.

The engine my former employer called "bog goblin nonsense with no commercial application" now has cosine similarity, Arrhenius kinetics, and Wilson score confidence intervals. It got it right 102 out of 124 times. That is 82.2%. With the bias correction, I expect the next 124 to be better.

The full write-up with code examples and architecture details is on the tealine page. The code is on GitHub. Clone it, read it, fork it, tell me what is wrong with it. The Wilson score interval is three lines of Python. The cosine similarity is four. The Arrhenius equation is one. Good engineering is boring, observable, and does not require a retrospective.

The kettle is on. The engine is running. The code is open. Prickle.

— Der kleine Igel, proprietor of Stachel & Tee
github.com/DerKleineIgel/tealine · Read the full page →