Designing System Architecture with Hierarchy and Layers using gramit.io

Gramit screen

Most architecture diagrams look clean at the beginning. A few boxes, a few arrows, everything is easy to follow. Then the system evolves…

New services, async flows, edge cases, integrations. What used to be simple quickly turns into a dense, hard to read diagram. At some point, people stop using it.

This is not a tooling limitation. It is a modeling problem.

The Problem with Flat Diagrams

Traditional tools like Miro, Draw.io, and Excalidraw push you into a flat structure:

  • each step becomes a separate box
  • each interaction becomes an arrow
  • logic gets distributed across the diagram

This works for small systems. It fails for real ones.

The core issue is that logic has no clear place to live.
You end up spreading behavior across multiple disconnected nodes, which makes the diagram harder to understand than the system itself.

The Shift: From Drawing to Structuring

Gramit introduces a different approach.

Instead of thinking in terms of connecting boxes, you start thinking in terms of structuring logic inside components.

  • nodes are not just visual elements
  • they are containers of behavior
  • complexity is grouped, not scattered

This aligns much more closely with how we design systems in code.

Start Simple: Define Boundaries First

At the top level, your goal is not to explain everything. It is to define the shape of the system.

Focus on:

  • API layer
  • core services
  • external integrations
  • data layer

Keep this view intentionally minimal. A good top level diagram should be understandable in seconds.

Gramit screen

Push Complexity Inward

The biggest mistake in architecture diagrams is expanding outward instead of inward.

When something becomes complex, the instinct is to add more boxes. This is what creates visual noise.

With Gramit, you do the opposite:

  • open the node
  • describe the internal flow
  • keep the external view clean

Inside a service, for example, you might represent:

  • input validation
  • orchestration logic
  • external calls
  • persistence
  • event dispatch

All of that lives in one place.

Ordering node expanded preview

Model Flows Without Breaking the Diagram

Flows are where most diagrams collapse.

As soon as you introduce queues, async processing, or retries, everything becomes harder to follow.

Gramit allows you to represent flows while preserving structure:

  • show interactions between components
  • keep internal logic encapsulated
  • avoid crossing arrows and duplication

This makes even complex flows readable.

Order flow within the Ordering node

Think in Layers and Hierarchy

A good diagram should behave like code:

  • top level shows architecture
  • inner levels show behavior
  • deeper levels show implementation details

If everything is on the same level, the diagram is already broken.

Hierarchy gives you control over how much detail is visible at each moment. This is what keeps diagrams scalable.

Why This Approach Works

This way of modeling brings a few practical benefits:

  • faster understanding of the system structure
  • reduced visual noise
  • easier onboarding for new engineers
  • diagrams that stay useful as the system evolves

More importantly, the diagram becomes a faithful representation of how the system actually behaves, not just how components are connected.

Final Thought

Most diagrams try to show everything at once and fail.

A better approach is to control where complexity lives:

  • keep the top level simple
  • move details inside nodes
  • use hierarchy to scale understanding

Gramit works because it enforces this discipline.

And that is what turns a diagram from something you draw once into something you actually use.

Check out https://gramit.io

Building an Inference OS: deterministic-first router for prediction markets

Building an Inference OS for prediction markets

Most AI agent stacks default to “throw the prompt at GPT-4o, hope for the best.” For prediction markets that’s expensive AND wrong — most market questions don’t need a paid LLM at all. Here’s how we built a 6-hook deterministic-first inference router on top of Kairon Forge.

The 6 hooks (in priority order)

  1. Market Regime classifier — 5 deterministic regimes (whale_dominant / meme_volatile / macro_anchored / panic_liquidation / dead_liquidity). Confident classification short-circuits the entire router. Zero LLM call.
  2. Anomaly detector — 3σ price spike + sentiment divergence. Confident anomaly FORCES Tier-2 (paid Claude/Anthropic), bypassing the viability cost cap on rare-and-important markets.
  3. Time-to-Resolution decay — exponential confidence decay vs event horizon. Low decayed confidence forces Tier-1 (Haiku-only).
  4. Persona overlay — 5 archetype priors (calibrated_researcher / whale_mimic / panic_seller / momentum_trader / contrarian) adjust baseline confidence.
  5. Panic mode circuit breaker — 60s rolling burn-rate σ. >2σ from baseline → force Ollama-only.
  6. Economic Viability Filter — per-tier hard cost cap (Free $0.05 / Pro $0.50 / Elite $5 / Enterprise $100). >cap → 402 quotaExhausted.

Cost-aware Cognition

Before every paid call, EIG / cost ratio gate (shouldEscalate(eig, cost, threshold=0.5)). Information gain ÷ inference cost. Below threshold → collapse to Tier-1 + budget consumption note.

Test coverage

350+ inference tests covering router decision boundaries. Components: budget consumption gate, complexity classifier (trivial / medium / rare_hard), Tier-2 dispatch, recursion-depth + context-bloat guards, reflection-loop + duplicate-prompt detection.

Why this matters

Cursor’s silent auto-upgrade on quota exhaustion triggered viral brand backlash + US state class-action allegations. We engineered a structural answer: tier caps, panic mode, no-auto-charge — all enforced at the router layer.

Source: github.com/greymoth-jp · Live: kairon.trade

This is part of the API Kernel work at services/kairon-guardian/ — happy to answer architecture questions.

We’re hiring a DevOps Content Engineer – Remote LATAM

We’re building the agentic OS for DevOps — AI agents that make
cloud environments self-building, self-healing, and self-optimizing.

We’re looking for a DevOps/SRE engineer who also creates technical
content. If you’ve spent years provisioning infrastructure, responding
to incidents, and somewhere along the way started writing about it —
this role is for you.

What you’ll do

  • Write technical blog posts, tutorials, and guides from real hands-on experience
  • Create demo videos and screencasts showing the product in realistic scenarios
  • Build proof-of-concepts, sandbox environments, and reference architectures
  • Engage with DevOps and SRE communities
  • Work with Marketing, Product, and Engineering to align content with what matters

What we’re looking for

  • 3–5 years in DevOps, SRE, or related technical role
  • Hands-on experience with Terraform, Kubernetes, Docker, AWS/GCP/Azure
  • Familiarity with Prometheus, Grafana, or OpenTelemetry
  • You’ve created technical content before — blog, talks, videos, GitHub
  • Excellent written and verbal English
  • Based in LATAM

Nice to have

  • Experience with video editing tools
  • Active presence in developer communities
  • Public speaking experience

What we offer

  • Full-time, 100% remote
  • Flexible work arrangements
  • Competitive compensation

Interested? Apply here: https://jobs.ashbyhq.com/the-global-talent-co/f9495aa9-7228-424d-889f-101747a816b4

Antigravity is Dead Long Live Antigravity.

Google just dropped Antigravity 2.0 at I/O 2026—and in the same breath, it killed Gemini CLI, stranded a lot of chat histories in a backup directory, and shipped three new surfaces sharing the same brand. If you opened the app this morning and panicked because your conversations are gone, keep reading. We’ll fix it.

What just happened at I/O 2026

On May 18, 2026, Google rebranded Antigravity from a “cursor competitor” into a full agent-first development platform powered by Gemini 3.5 Flash. The brand now spans four products:

  • Antigravity 2.0 — the new standalone desktop app with multi-agent orchestration
  • Antigravity CLI — a Go-based terminal tool replacing Gemini CLI
  • Antigravity SDK — programmatic access to the same agent harness Google uses internally.
  • Antigravity IDE — the original VS Code-style app from November 2025, still alive
    And here’s the deadline you can’t ignore: Gemini CLI sunsets on June 18, 2026 for AI Pro, AI Ultra, and free-tier users. Enterprise on Gemini Code Assist Standard or Enterprise keeps access — everyone else has 30 days to migrate.

The Four Faces of Antigravity

Antigravity 2.0 (Desktop App)

The flagship. Runs multiple agents in parallel, ships dynamic subagents for parallelized workflows, schedules tasks in the background, and integrates natively with AI Studio, Firebase, and Android. Export a project from AI Studio on your phone and open it locally, full context intact. Voice command support is in too — you can talk to your agents instead of typing.

Antigravity CLI

Rebuilt from scratch in Go. Same agent harness as the desktop, terminal-native, and noticeably faster than its predecessor. If you live in Gemini CLI today, this is your migration target — and you have until June 18 to switch.

# Verify your install once you migrate
antigravity --version
antigravity agent new "refactor the auth module"

Antigravity SDK

For building custom agents on top of Google’s coding infrastructure, optimized for Gemini models. You define the behaviors; you host wherever it fits — Google Cloud, your own infra, your choice. Custom agent templates are available in AI Studio for enterprise use cases.

Antigravity IDE (the original)

Still maintained. Still the VS Code fork from November 2025. This is the one most of us got hooked on — and it’s the one with the migration trap (more on that below).

The immediate pain: your chat history is in the wrong directory

If you updated to 2.0 and your conversations are gone, your data is not lost — the migration stranded it in a backup folder.

Antigravity 2.0 uses three directories under ~/.gemini/:

Directory Owner
~/.gemini/antigravity Live data for the standalone 2.0 app
~/.gemini/antigravity-ide Live data for the original IDE
~/.gemini/antigravity-backup Snapshot the migration created — often more complete than the live dir

After the update, the standalone app’s antigravity/ dir frequently ends up missing brain entries, scratch space, and conversation files. The antigravity-backup/ next to it has the full set. We just need to put the right one in front of the app.

The fix: rsync from backup into the live dir

macOS / Linux

# Dry-run first — see exactly what would change
rsync -avhn --exclude='mcp_config.json' 
  ~/.gemini/antigravity-backup/ 
  ~/.gemini/antigravity/

# Looks right? Run it for real (drop the -n flag)
rsync -avh --exclude='mcp_config.json' 
  ~/.gemini/antigravity-backup/ 
  ~/.gemini/antigravity/

Why exclude mcp_config.json? It’s usually a symlink pointing at your local MCP server configuration — overwrite it and you break your active MCP connections.

Windows (PowerShell)

$src  = "$env:USERPROFILE.geminiantigravity-backup"
$dest = "$env:USERPROFILE.geminiantigravity"

# Robocopy mirror, excluding mcp_config.json
robocopy $src $dest /E /XF mcp_config.json

After running:

  1. Fully quit Antigravity — kill the process, don’t just close the window.
  2. Reopen the app.
  3. Verify the side panel now shows your threads.
    If anything looks off, your antigravity-backup/ is untouched — you can re-run the sync or copy specific files manually. For peace of mind, snapshot antigravity-backup/ to an external location before you start poking at it.

How to install just the IDE (skip 2.0)

The good news: the IDE and the standalone 2.0 app keep their data in separate directories (antigravity-ide/ vs antigravity/), so your IDE history doesn’t get clobbered when 2.0 installs alongside it.

The bad news: the installers conflict at the OS level. Install both and the IDE binary refuses to launch — dependencies are coupled. Uninstalling 2.0 doesn’t fully heal the IDE either; you have to reinstall the IDE cleanly, which wipes your IDE settings.

If you want to stay on the IDE only:

  1. Uninstall Antigravity 2.0 completely if you already installed it.
  2. Back up ~/.gemini/antigravity-ide before touching anything else.
  3. Grab the IDE installer from the releases page: antigravity.google/releases
  4. Pick a version that predates the 2.0 split — 1.23.2 is the safest landing pad.
  5. In Settings, switch updates to manual so the app doesn’t silently jump back to 2.0.
// settings.json
{
  "update.mode": "manual"
}

Migration checklist

  • [ ] Migrate Gemini CLI workflows to Antigravity CLI before June 18, 2026
  • [ ] Pick one: 2.0 desktop or the original IDE — never install both
  • [ ] Back up ~/.gemini/antigravity and ~/.gemini/antigravity-ide weekly
  • [ ] If history disappeared post-update, rsync from ~/.gemini/antigravity-backup (exclude mcp_config.json)
  • [ ] Set update.mode to manual if you depend on the IDE
  • [ ] Evaluate the new $100 AI Ultra tier if you run multi-agent workloads (5× the Pro limits)
    ## TL;DR

Antigravity 2.0 + CLI + SDK shipped at I/O 2026. Gemini CLI dies June 18, 2026. Lost your chat history? Your data is sitting in ~/.gemini/antigravity-backup — rsync it into ~/.gemini/antigravity (excluding mcp_config.json), restart the app. Want the old IDE only? Uninstall 2.0, install 1.23.2 from the releases page, set updates to manual. Pick one — don’t run 2.0 and the IDE side by side.

The king is dead. Long live the king. Migrate carefully.

24 horas después de abrir: las primeras consultas, y la primera venta que ya di por perdida

Usuarios de GA4 por país

Originally published at moday.me. Building MODAY in public.

24 horas después de abrir: las primeras consultas, y la primera venta que ya di por perdida

Abrí el 18/5. El 19/5 llegaron dos correos.

Abrí la tienda el 18/5, tal como estaba previsto.

El post anterior lo cerré con eso de «abrir con el miedo todavía puesto». Al día siguiente de abrir, entraron dos correos.

El primero:

Hi! Quick question — do you ship to Chicago, Dallas, San Jose
or is your delivery limited to certain areas?
Also, how long does it usually take for orders to arrive there?

El segundo:

Can you deliver to Bavaria, Germany?

Voy a ser honesto: me dio un subidón importante.

Chicago, Dallas, San Jose, Baviera. Alguien cuyo nombre y cuya cara nunca voy a conocer había llegado hasta la tienda que monté yo, y se había interesado lo suficiente como para decidir mandar un correo. Esa clase de evidencia me cayó delante por primera vez.

Abrí GA4. Estados Unidos estaba por encima de Japón.

En paralelo, miré GA4. La primera foto a 24 horas desde la apertura.

Usuarios de GA4 por país

  • United States: 22 users (44%)
  • Japan: 14 users (28%)
  • Germany: 6 users (12%)
  • Singapore: 3 users
  • Canada: 2 users
  • South Korea: 1 user

El país en el que vivo, desde el que toqueteo la tienda todos los días, queda por detrás de Estados Unidos. Alemania, tercera.

Ese fue el momento en el que el geo-routing y el setup de nueve idiomas devolvieron algo a cambio — por primera vez, en números. En el post anterior escribí «no veo al cliente». La cara todavía no se enfoca. Pero la geografía aproximada de dónde está empieza a asomar.

Para que conste: pedidos siguen en cero. El interés es real, pero todavía no convierte. Algo falta — ansiedad sobre envíos, plazos, precio, el look. No sé cuál de ellos, y no lo voy a saber hasta que pueda observarlo. Ese es el punto en el que estoy ahora mismo.

No pude contestar el mismo día.

Vuelvo a los correos.

Llegaron los dos. Lo agradezco. Pero no fui capaz de contestar el mismo día en el que entraron.

Los vi de noche, tarde, y esa noche tenía trabajo del curro principal corriendo. Redacté la respuesta en japonés, pasé las traducciones por Claude Code al inglés y al alemán, y los mensajes salieron, en realidad, al día siguiente.

La parte de redactar + traducir, una vez tienes el flujo montado, son cinco o diez minutos. Pero la latencia entre «entró el correo» y «estoy delante de la pantalla» ya añade retraso. Encima, para contestar a una pregunta sobre disponibilidad de envío hay que comprobar los países que cubre Gelato, estimar plazos — montar una respuesta seria pide un trozo de concentración.

En el correo como canal, contestar dentro de 24 horas no se considera mala educación. Pero la temperatura del otro lado se cae bastante en 24 horas.

La diferencia entre una tienda donde «mandas una pregunta y vuelve la respuesta rápido» y una donde «la respuesta vuelve al día siguiente» probablemente cambia la decisión de compra. Sobre todo cuando es una marca nueva, envía al extranjero, y el cliente ha pagado el coste de escribir en inglés.

Eso fue un coste de oportunidad claro. El primero.

Así que ese mismo día metí un chatbot.

Después de mandar las respuestas tarde, me moví rápido.

Las mismas preguntas van a volver. Disponibilidad de envío, plazos, métodos de pago, tallaje. Contestarlas una a una por correo es demasiado lento como soporte.

Ese mismo día metí un widget de chat en la esquina inferior derecha de la tienda.

Está montado directamente sobre la Claude API, con soporte para nueve idiomas. Embebido en el tema de Shopify, responde en el locale del visitante de forma automática. Las respuestas de primera línea sobre envíos, plazos, tallas y pagos ya viven ahí.

Tiempo de construcción: unos 15 minutos. Le pedí a Claude Code, «mete un widget de chat abajo a la derecha, rutea los mensajes a través de la Claude API, ajusta el idioma al locale de Shopify del visitante» — y funcionó.

El estado de la tienda al final de ese día: «si entra la misma pregunta otra vez, el cliente no espera 24 horas». Esa era la jugada disponible ese día.

Arrancó el bucle de observación.

Hasta este punto, toda la fase de construcción funcionaba a base de «hipótesis que me había montado dentro de la cabeza».

La camiseta del día de la semana debería pegar con ingenieros y geeks. El internacional debería tener sentido. El modelo de nueve idiomas, multidivisa, sin inventario, debería funcionar. Todo era «debería».

Eso cambió en el momento en el que abrí.

GA4 empieza a devolver desgloses por país. Alguien manda una pregunta concreta por correo. Los logs del chat empiezan a acumular qué es lo que de verdad inquieta a los visitantes.

El mundo ha empezado a contestar.

Sigo sin tener «una forma de vender». El primer coste de oportunidad ya ocurrió. Pero como el bucle de observación está en marcha, la siguiente jugada ya no es a ciegas.

El principio real de una marca puede que no sea cuando la ingeniería está terminada, ni cuando la tienda abre — puede que sea cuando el bucle de observación arranca a girar, y esa es la parte que empezó hoy.

Pronto más.

— Yoskee

moday.me

Ponte el día. — Las camisetas MODAY

Set Piezas Precio
La semana completa → Mon–Sun (7) $159
La semana laboral → Mon–Fri (5) $119
Paquete Inicial → Mon · Wed · Fri (3) $79
El fin de semana → Sat · Sun (2) $55

Envío gratis a partir de $99 · 8 colores × 6 tallas · 9 idiomas

24 Stunden nach Launch: die ersten Anfragen — und der erste Verkauf, den ich schon verloren habe

GA4 Nutzer nach Land

Originally published at moday.me. Building MODAY in public.

24 Stunden nach Launch: die ersten Anfragen — und der erste Verkauf, den ich schon verloren habe

Am 18.05. aufgemacht. Am 19.05. lagen zwei Mails im Posteingang.

Ich habe den Shop am 18.05. aufgemacht. Wie geplant.

Der letzte Post endete mit „aufmachen, mit der Angst, die noch dranhängt”. Den Tag darauf kamen zwei Mails rein.

Die erste:

Hi! Quick question — do you ship to Chicago, Dallas, San Jose
or is your delivery limited to certain areas?
Also, how long does it usually take for orders to arrive there?

Die zweite:

Can you deliver to Bavaria, Germany?

Ganz ehrlich: ich war aus dem Häuschen.

Chicago, Dallas, San Jose, Bayern. Irgendjemand, dessen Namen und Gesicht ich nie kennen werde, hat zu dem Shop gefunden, den ich gebaut habe — und war neugierig genug, sich aktiv hinzusetzen und eine Mail zu schreiben. Diese Art von Evidenz lag zum allerersten Mal vor mir.

GA4 aufgemacht — die USA lagen vor Japan.

Parallel dazu habe ich in GA4 reingeschaut. Der erste 24-Stunden-Snapshot nach dem Launch.

GA4 Nutzer nach Land

  • United States: 22 users (44%)
  • Japan: 14 users (28%)
  • Germany: 6 users (12%)
  • Singapore: 3 users
  • Canada: 2 users
  • South Korea: 1 user

Das Land, in dem ich sitze, das Land, von dem aus ich jeden Tag am Shop herumdrücke, liegt hinter den USA auf Platz zwei. Deutschland kommt auf Platz drei.

Das war der Moment, in dem das Geo-Routing und das Setup mit neun Locales zum ersten Mal in Zahlen aufgegangen sind. Im letzten Post habe ich geschrieben: „Ich sehe den Kunden nicht.” Das Gesicht ist immer noch nicht scharf. Aber die grobe Geografie, wo diese Leute sitzen, fängt an, sich abzuzeichnen.

Zur Klarstellung: Bestellungen stehen weiterhin bei null. Interesse ist da, es konvertiert nur noch nicht. Irgendetwas fehlt — Versand-Unsicherheit, Lieferzeit, Preis, der Look. Welches davon, weiß ich noch nicht, und ich werde es erst wissen, wenn ich es beobachten kann. Genau da stehe ich gerade.

Ich habe es nicht geschafft, am selben Tag zu antworten.

Zurück zu den Mails.

Zwei sind reingekommen. Dafür bin ich dankbar. Aber ich habe es nicht geschafft, am selben Tag zu antworten, an dem sie ankamen.

Ich habe sie spät abends gesehen, und ich hatte an dem Abend noch Brotjob-Arbeit am Laufen. Ich habe die Antwort auf Japanisch geschrieben, sie mit Claude Code ins Englische und Deutsche übersetzt — und die Mails sind tatsächlich erst am Tag darauf rausgegangen.

Das Drafting plus Übersetzen selbst sind, wenn man eingespielt ist, 5–10 Minuten. Aber die Latenz zwischen „Mail kam rein” und „ich sitze vor dem Bildschirm” zieht es schon auseinander. Dazu kommt: eine Versandfrage seriös beantworten heißt, Gelatos abgedeckte Länder zu checken, Lieferzeiten abzuschätzen — eine richtige Antwort zusammenzubauen kostet einen echten Fokusblock.

Für E-Mail als Kanal gilt: innerhalb von 24 Stunden zu antworten ist nicht unhöflich. Aber die Temperatur auf der Gegenseite fällt in 24 Stunden ziemlich deutlich.

Der Unterschied zwischen einem Shop, bei dem „du schickst eine Frage rein und kriegst schnell eine Antwort zurück”, und einem, bei dem „die Antwort kommt am nächsten Tag” — der verändert wahrscheinlich die Kaufentscheidung. Erst recht bei einer Marke, die der Kunde zum ersten Mal sieht, die international verschickt, und bei der er die Mühe in Kauf genommen hat, auf Englisch zu schreiben.

Das war glasklar Opportunitätskosten. Die ersten.

Also habe ich am selben Tag einen Chatbot rausgehauen.

Nachdem die verspäteten Antworten draußen waren, bin ich sofort drangegangen.

Dieselben Fragen kommen wieder. Versandverfügbarkeit, Lieferzeit, Bezahlmethoden, Größen. Das einzeln per Mail abzuarbeiten ist als Support viel zu langsam.

Noch am selben Tag habe ich ein Chat-Widget unten rechts in den Shop reingesetzt.

Aufgesetzt direkt auf der Claude API, neun Sprachen. In das Shopify-Theme eingebettet, antwortet automatisch in der Locale des Besuchers. Erste-Linie-Antworten zu Versand, Lieferzeit, Größen und Bezahlung liegen jetzt da drin.

Build-Zeit: ungefähr 15 Minuten. Ich habe Claude Code gesagt „setz unten rechts ein Chat-Widget hin, route Nachrichten durch die Claude API, Sprache nach Shopify-Locale des Besuchers” — und es lief.

Status des Shops am Ende dieses Tages: „falls dieselbe Frage gleich nochmal reinkommt, wartet der Kunde keine 24 Stunden mehr.” Das war der Spielzug, der an diesem Tag möglich war.

Die Beobachtungsschleife läuft.

Bis zu diesem Punkt lief die gesamte Build-Phase auf „Hypothesen, die ich mir in meinem eigenen Kopf zurechtgelegt habe”.

Wochentags-Shirts müssten bei Engineers und Geeks landen. International müsste Sinn ergeben. Das Modell mit neun Locales, mehreren Währungen, null Lagerbestand müsste funktionieren. Alles „müsste”.

In der Sekunde, in der ich aufgemacht habe, hat sich das geändert.

GA4 liefert plötzlich Country-Breakdowns. Jemand schickt eine konkrete Frage per Mail. Die Chat-Logs fangen an einzusammeln, worüber sich die Besucher tatsächlich Gedanken machen.

Die Welt hat angefangen zu antworten.

Eine „Methode zum Verkaufen” habe ich immer noch nicht. Die ersten Opportunitätskosten sind schon angefallen. Aber weil die Beobachtungsschleife jetzt läuft, ist der nächste Spielzug kein Bauchgefühl mehr.

Der eigentliche Start einer Marke ist vielleicht weder der Moment, an dem die Engineering-Arbeit fertig ist, noch der Moment, an dem der Shop aufmacht — sondern der Moment, an dem die Beobachtungsschleife anfängt, sich zu drehen. Und genau dieser Teil hat heute begonnen.

Bis bald.

— Yoskee

moday.me

Trag den Tag. — Hol dir die MODAY-Shirts

Set Teile Preis
Die ganze Woche → Mon–Sun (7) $159
Die Arbeitswoche → Mon–Fri (5) $119
Starter-Pack → Mon · Wed · Fri (3) $79
Das Wochenende → Sat · Sun (2) $55

Gratisversand ab 99 $ · 8 Farben × 6 Größen · 9 Sprachen

Monté una tienda capaz de venderle al mundo entero. Y no consigo ver a quién le estoy vendiendo.

Originally published at moday.me. Building MODAY in public.

Monté una tienda capaz de venderle al mundo entero. Y no consigo ver a quién le estoy vendiendo.

Quedan cinco días, y lo que más miedo me da es el silencio liso

Faltan cinco días para que MODAY abra el 18/5.

Hasta ahora he ido escribiendo posts más bien optimistas. El stack se armó en tres días porque se lo pasé todo a Claude. La pipeline de distribución se abre sola en nueve idiomas. La fontanería funciona.

Todo eso es verdad. Por el lado de ingeniería, hay tracción real.

Lo que ahora mismo me da miedo es algo más aburrido que cualquiera de esas cosas.

Se abre la puerta. No entra nadie. Cero pedidos. La versión llana del miedo.

«Todavía no sabemos si se va a vender» — esa parte tiene sentido, así arranca cualquier lanzamiento. La parte más jodida es la siguiente capa: tampoco sé qué haría que se venda.

No consigo imaginarme un marketing-que-funcione para un público internacional

MODAY se montó global desde el día uno. Nueve idiomas, varias monedas, impresión local en cada región vía Gelato, Shopify Markets ocupándose del enrutado. Como sistema para vender, está abierto al mundo.

Lo que no tengo es una imagen clara de cómo meter gente dentro de ese sistema.

Más exacto: no consigo imaginar un marketing internacional que funcione de verdad. El «de verdad» es la palabra que aguanta el peso. Formas hay de sobra. Postear en redes. Montar SEO. Mandar notas a prensa. Escribir en Reddit. Lanzar en Product Hunt. Tirar shorts en TikTok. La lista de jugadas me la sé.

Lo que no veo es la versión que recupera los costes iniciales. Esa es la respuesta honesta.

Pagado, ahora mismo, está fuera de la mesa

El camino más rápido sería pagado: Meta, Google, TikTok ads. Sobre todo para un público internacional, meter presupuesto en pagado es la forma más rápida de cerrar bucles iniciales de validación.

Pero no tengo presupuesto. Es una operación de una sola persona, y no hay gasto publicitario. Hecho simple.

Esto no es un alegato contra pagado — es cuestión de orden. En cuanto vea la forma aproximada de quién compra y por qué, pagado es un amplificador obvio. Hasta entonces toca encontrar tracción sin él.

El vídeo corto es lo único que medio consigo imaginar

El único canal que medio puedo dibujar en la cabeza es el vídeo corto en redes.

El producto es lo bastante visual como para que el «ah, qué buena» entre en un solo fotograma. Alguien con la camiseta «MONDAY: System Booting…», sentado frente a la cámara un lunes por la mañana con un café en la mano, un solo plano. Para el bolsillo cultural adecuado, creo que ese fotograma puede viajar.

Los mock-ups en vídeo los puedo montar con IA — Fashn.ai para componer al modelo, Kling o HeyGen para el movimiento. Ese stack ya lo tengo esbozado.

Pero todavía no está corriendo. El stack existe en mi cabeza; la implementación va por detrás.

No voy a saber qué funciona hasta que empiece a publicar esos vídeos y mire en directo qué engancha. La fontanería me la puede montar la IA. La parte difícil es qué mostrar, y a quién.

El problema de verdad — no veo al cliente

Quita la ansiedad por los canales y la verdad de debajo es más simple.

No veo al cliente.

Antes escribí que «las camisetas día-de-la-semana deberían pegar con ingenieros y geeks». Sigue siendo una hipótesis. Quién la compra de verdad, dónde vive, en qué estado de ánimo está cuando decide comprar — nada de eso se me ha resuelto en una imagen real en la cabeza.

La escena que querría poder dibujar, y que aún no puedo: alguien de 28 años en Berlín escribiendo Rust, scrolleando Reddit un viernes por la noche, y por el feed le pasa una camiseta «FRIDAY: Build Successful ✓», y se ríe, y la mete en el carrito. Esa escena concreta, con una cara real puesta encima.

Tengo algo borroso. Pero la cara no está. En qué idioma le sale la página. Qué dispositivo. Qué humor. La resolución es demasiado baja.

Sin esa resolución, no puedo decidir qué filmar. No puedo decidir cuál de los nueve idiomas empujar primero. Antes que cualquier táctica de adquisición está la tierra en la que crecen las tácticas, y todavía no tengo la tierra.

Esto no es algo que la IA me pueda contestar. La IA puede sacarme consejo genérico sobre «marketing para ingenieros» hasta el infinito. Pero la pregunta de verdad — quién, en concreto, es el cliente de MODAY — solo se resuelve cuando estoy sobre el terreno mirándolo pasar. Esa observación la IA no la tiene. La tengo que tener yo.

O sea: no voy a ver al cliente hasta que la puerta esté abierta.

No tengo una forma de vender. Tengo una forma de ir tachando formas de no-vender.

El 18/5 no se mueve. Ese día se abre la puerta.

Lo más probable es que al principio no entre nadie. Y los que entren puede que no compren. Pero en cuanto la puerta esté abierta, empieza a llegar señal por primera vez.

Qué página de qué idioma se vio. Qué camiseta de qué día se metió al carrito. Qué fotograma de qué vídeo hizo que alguien se parase medio segundo.

«Una forma de vender» ahora mismo no la tengo. Lo que sí voy a tener, en cuanto abramos, es el proceso lento de ir tachando, una a una, las formas que no venden.

Ahí probablemente arranca la marca de verdad.

El miedo es real. Voy a abrir con el miedo todavía puesto.

Pronto más.

— Yoskee

moday.me

Ponte el día. — Las camisetas MODAY

Set Piezas Precio
La semana completa → Mon–Sun (7) $159
La semana laboral → Mon–Fri (5) $119
Paquete Inicial → Mon · Wed · Fri (3) $79
El fin de semana → Sat · Sun (2) $55

Envío gratis a partir de $99 · 8 colores × 6 tallas · 9 idiomas

Ich habe einen Shop, der weltweit verkaufen kann. Ich sehe nur nicht, an wen.

Originally published at moday.me. Building MODAY in public.

Ich habe einen Shop, der weltweit verkaufen kann. Ich sehe nur nicht, an wen.

Fünf Tage vor Launch — und am meisten Angst macht mir die Stille

Noch fünf Tage bis MODAY am 18.05. aufmacht.

Bisher habe ich hier eher zuversichtliche Posts geschrieben. Stack stand in drei Tagen, weil ich alles an Claude abgegeben habe. Die Distribution-Pipeline fächert sich automatisch in neun Sprachen auf. Die Sanitärinstallation funktioniert.

Das stimmt auch alles. Auf der Engineering-Seite habe ich echten Boden unter den Füßen.

Was mir jetzt gerade Angst macht, ist viel banaler.

Tür auf. Niemand kommt rein. Null Bestellungen. Die nüchterne Variante der Angst.

„Wir wissen noch nicht, ob’s verkauft” — okay, das ist normal, so fängt jeder Launch an. Die nächste Schicht darunter ist die unangenehmere: ich weiß auch nicht, was es zum Verkaufen bringen würde.

Ich kann mir kein „funktionierendes” Marketing für ein internationales Publikum vorstellen

MODAY war von Tag eins global gedacht. Neun Sprachen, mehrere Währungen, Gelato druckt regional, Shopify Markets übernimmt das Routing. Als Verkaufssystem ist der Laden zur Welt hin offen.

Was mir fehlt, ist ein klares Bild davon, wie ich Leute in dieses System hineinbekomme.

Genauer gesagt: ich kann mir kein internationales Marketing vorstellen, das wirklich funktioniert. Das „wirklich funktioniert” ist das tragende Wort. An Formaten mangelt es nicht. Auf Social posten. SEO aufsetzen. Pressetexte rausschicken. Auf Reddit schreiben. Auf Product Hunt launchen. TikTok-Shorts drehen. Die Spielzüge kenne ich alle.

Was ich nicht sehe, ist die Variante, die die Anfangskosten wieder einspielt. Das ist die ehrliche Antwort.

Bezahlte Anzeigen sind aktuell vom Tisch

Der schnellste Weg wäre Paid: Meta, Google, TikTok Ads. Gerade bei einem internationalen Publikum ist Budget reinkippen die kürzeste Schleife für erste Validierung.

Aber ich habe das Budget nicht. Einzelunternehmen, kein Ad-Spend. Schlichte Tatsache.

Das ist kein Argument gegen Paid — es ist eine Frage der Reihenfolge. Sobald ich grob sehe, wer aus welchem Grund kauft, ist Paid offensichtlich ein guter Verstärker. Bis dahin muss Traction ohne Anzeigen kommen.

Short-Form-Video ist das Einzige, was ich mir halbwegs vorstellen kann

Der einzige Kanal, zu dem ich überhaupt ein Bild im Kopf habe, ist Short-Form-Video auf Social.

Das Produkt ist visuell genug, dass „oh, geil” in einem einzigen Frame ankommt. Jemand im „MONDAY: System Booting…”-Shirt, Montagmorgen, Kaffee in der Hand, vor der Kamera. Ein einziger Cut. Für die richtige kulturelle Nische glaube ich, dass dieser Frame trägt.

Die Video-Mockups kann ich mit KI bauen — Fashn.ai für die Modellkomposition, Kling oder HeyGen für die Bewegung. Stack steht skizziert.

Aber er läuft noch nicht. Der Stack existiert in meinem Kopf; die Implementierung hängt hinterher.

Ich werde erst dann wirklich wissen, was zieht, wenn ich diese Videos rauslasse und schaue, was hängenbleibt. Die Sanitärinstallation kann KI für mich bauen. Der schwierige Teil ist: was zeigen — und wem?

Das eigentliche Problem — ich sehe den Kunden nicht

Wenn ich die Marketing-Kanal-Anspannung wegnehme, ist die Wahrheit darunter simpler.

Ich sehe den Kunden nicht.

Ich habe vorher geschrieben, „Wochentags-Shirts müssten bei Engineers und Geeks landen”. Das ist immer noch eine Hypothese. Wer es tatsächlich kauft, wo diese Person lebt, in welcher Stimmung sie die Kaufentscheidung trifft — nichts davon ist in meinem Kopf zu einem echten Bild zusammengeschossen.

Das Bild, das ich gern zeichnen können möchte, aber noch nicht kann: ein 28-Jähriger in Berlin, schreibt Rust, scrollt Freitagabend durch Reddit, ein „FRIDAY: Build Successful ✓”-Shirt rutscht durch seine Timeline, er lacht kurz, legt es in den Warenkorb. Genau diese konkrete Szene, mit einem echten Gesicht dran.

Ich habe etwas Verschwommenes. Aber das Gesicht ist nicht da. In welcher Sprache ist die Seite für ihn. Welches Gerät. Welche Stimmung. Die Auflösung ist zu niedrig.

Ohne diese Auflösung kann ich nicht entscheiden, was ich filmen soll. Ich kann nicht entscheiden, welche der neun Sprachen ich zuerst pushe. Vor jeder Akquise-Taktik kommt der Boden, in dem die Taktiken wachsen — und den Boden habe ich noch nicht.

Das ist nichts, was KI für mich beantworten kann. KI kann endlos generisches „Marketing für Engineers”-Geschwafel produzieren. Aber die eigentliche Frage — wer, konkret, ist MODAYs Kunde — löst sich erst auf, wenn ich draußen im Feld beobachte, wie es passiert. Diese Beobachtung hat die KI nicht. Die muss ich machen.

Also: ich sehe den Kunden nicht, bevor die Tür offen ist.

Ich habe keinen Weg zu verkaufen. Ich habe einen Weg, Nicht-Verkaufen wegzustreichen.

Der 18.05. verschiebt sich nicht. An dem Tag geht die Tür auf.

Wahrscheinlich kommt am Anfang niemand rein. Wer reinkommt, kauft vielleicht nicht. Aber sobald die Tür offen ist, kommt zum ersten Mal Signal an.

Welche Sprachseite wurde aufgerufen. Welches Wochentags-Shirt landete im Warenkorb. Welcher Video-Frame hat jemanden für eine halbe Sekunde anhalten lassen.

Einen „Weg zu verkaufen” habe ich aktuell nicht. Was ich nach dem Öffnen haben werde, ist der langsame Prozess, die Dinge, die nicht verkaufen, einen nach dem anderen wegzustreichen.

Da fängt vermutlich die eigentliche Marke an.

Die Angst ist real. Ich mache mit der Angst auf, die noch dranhängt.

Bis bald.

— Yoskee

moday.me

Trag den Tag. — Hol dir die MODAY-Shirts

Set Teile Preis
Die ganze Woche → Mon–Sun (7) $159
Die Arbeitswoche → Mon–Fri (5) $119
Starter-Pack → Mon · Wed · Fri (3) $79
Das Wochenende → Sat · Sun (2) $55

Gratisversand ab 99 $ · 8 Farben × 6 Größen · 9 Sprachen

Braintrust Autoevals: CI Gates for LLM Regressions

LLM applications need a different kind of regression test. Unit tests can tell you whether a function returns a value, but they do not tell you whether an assistant quietly changed a refund action, dropped a required field, or returned valid JSON with the wrong business meaning. That gap is where evaluation tooling becomes practical engineering infrastructure instead of research theater.

Braintrust frames evaluations as a way to measure AI application quality, catch regressions before production, and compare system changes over time. The platform spans playground iteration, experiments, CI/CD, production scoring, and feedback loops. The smaller piece tested here is Autoevals, Braintrust’s evaluator library for scoring model outputs.

Effloow Lab ran a credential-free sandbox before writing this guide. The lab installed autoevals==0.1.0 and braintrust==0.19.0, built a tiny Python eval script, scored JSON outputs with ExactMatch and ValidJSON, ran a passing baseline, then triggered an intentional regression that exited with code 1. No Braintrust cloud project, API key, Claude API call, OpenAI API call, or paid model was used.

Effloow Lab — Local sandbox on macOS with Python 3.12.8. Lab run notes: data/lab-runs/braintrust-llm-eval-autoevals-ci-sandbox-poc-2026.md. The PoC validates a local scorer-and-exit-code pattern; it does not claim hosted Braintrust experiment logging or LLM-as-judge execution.

Why LLM Regression Tests Fail Late

Traditional CI works well when correctness is deterministic. A parser either accepts input or it does not. A migration either applies or it fails. A serializer either matches a snapshot or it differs. LLM features are less stable because the output is usually a distribution, not a single fixed return value.

That does not mean teams should skip CI. It means the CI gate needs to measure the right layer. A useful LLM eval suite usually combines:

  • Shape checks for JSON validity, schema compliance, field presence, and safe formatting.
  • Exact checks for outputs where the answer must be one of a few allowed labels or actions.
  • Semantic checks for summaries, retrieval quality, tool routing, and policy adherence.
  • Human review for subjective or high-risk cases.
  • Production trace feedback so the offline dataset does not freeze in time.

Braintrust’s evaluation docs describe offline evaluation as a controlled dataset-based workflow where expected outputs are known and code-based or LLM-as-judge scorers can be used. That is the right mental model for CI: select a small set of critical examples, run the application against those examples, score the outputs, and fail the build if quality drops below the accepted threshold.

What Braintrust and Autoevals Provide

Braintrust is the broader evaluation and observability platform. Its current Python SDK reference lists braintrust==0.19.0 and requires Python 3.10 or higher. The SDK includes evaluation, tracing, prompt, dataset, experiment, and integration surfaces. The same reference page lists integrations for Anthropic, OpenAI, OpenAI Agents, LangChain, LlamaIndex, LiteLLM, Pydantic AI, CrewAI, Temporal, and other agent frameworks.

Autoevals is the evaluator library. The current Python API reference identifies Autoevals Python API v0.1.0 and documents installation with pip install autoevals. It includes LLM evaluators such as Factuality, Summary, Translation, and LLMClassifier, but it also includes deterministic scorers such as ExactMatch, Levenshtein, NumericDiff, JSONDiff, and ValidJSON.

That split matters. You do not need to start with LLM-as-judge scoring or a hosted evaluation dashboard. For many production systems, the first useful eval is stricter and simpler: “Did the model return valid JSON, and did the required action stay the same?”

If you already use open-source observability, compare this with Effloow’s Langfuse self-hosting guide. Langfuse is a strong fit when self-hosted traces and prompt observability are the center of gravity. Braintrust is compelling when the core need is systematic experiments, datasets, scorers, and CI-grade regression tracking.

What the Sandbox Built

The sandbox created a small application that returns compact JSON for two cases:

DATASET = [
    {
        "id": "refund-status",
        "input": "Return a compact JSON refund status.",
        "expected": {"status": "pending", "action": "collect_receipt"},
    },
    {
        "id": "shipping-status",
        "input": "Return a compact JSON shipping status.",
        "expected": {"status": "ready", "action": "notify_customer"},
    },
]

The baseline mode returns the expected JSON. The regression mode changes one business action from notify_customer to email_customer. That is intentionally subtle: the output is still valid JSON, and a loose “does it parse?” check would pass. The business behavior is wrong.

The scorer uses two Autoevals checks:

from autoevals import ExactMatch, ValidJSON

exact = ExactMatch()
valid_json = ValidJSON()

For each case, the script computes an exact score and a JSON-validity score, averages them, and exits successfully only when the overall average is 1.0. This is deliberately strict for a CI gate. If the goal is regression safety, a changed action should fail.

Step 1: Create the Sandbox

Use a temporary directory and a virtualenv. Keep this out of your production repository until the eval shape is proven.

rm -rf /tmp/effloow-braintrust-autoevals-poc
mkdir -p /tmp/effloow-braintrust-autoevals-poc
cd /tmp/effloow-braintrust-autoevals-poc
python3 -m venv .venv

Pin the package versions used in this run:

autoevals==0.1.0
braintrust==0.19.0

Install them:

.venv/bin/python -m pip install -r requirements.txt

The sandbox install completed successfully with autoevals-0.1.0 and braintrust-0.19.0. A later production project can relax pins after compatibility testing, but pins are useful in an article PoC because they make the result easier to reproduce.

Step 2: Write a Minimal Eval Gate

Create eval_guardrail.py:

import json
import sys

from autoevals import ExactMatch, ValidJSON


DATASET = [
    {
        "id": "refund-status",
        "input": "Return a compact JSON refund status.",
        "expected": {"status": "pending", "action": "collect_receipt"},
    },
    {
        "id": "shipping-status",
        "input": "Return a compact JSON shipping status.",
        "expected": {"status": "ready", "action": "notify_customer"},
    },
]


def app(case, mode):
    if mode == "baseline":
        return json.dumps(case["expected"], separators=(",", ":"))
    if case["id"] == "shipping-status":
        return json.dumps({"status": "ready", "action": "email_customer"}, separators=(",", ":"))
    return json.dumps(case["expected"], separators=(",", ":"))


def numeric_score(result):
    if hasattr(result, "score"):
        return float(result.score)
    if isinstance(result, dict) and "score" in result:
        return float(result["score"])
    return float(result)


def run(mode):
    exact = ExactMatch()
    valid_json = ValidJSON()
    rows = []

    for case in DATASET:
        output = app(case, mode)
        expected = json.dumps(case["expected"], separators=(",", ":"))
        exact_score = numeric_score(exact(output=output, expected=expected))
        json_score = numeric_score(valid_json(output=output))
        combined = (exact_score + json_score) / 2
        rows.append({"id": case["id"], "exact": exact_score, "valid_json": json_score, "combined": combined})

    average = sum(row["combined"] for row in rows) / len(rows)
    print(json.dumps({"mode": mode, "average": average, "rows": rows}, indent=2))
    return 0 if average >= 1.0 else 1


if __name__ == "__main__":
    selected_mode = sys.argv[1] if len(sys.argv) > 1 else "baseline"
    raise SystemExit(run(selected_mode))

This is not pretending to be a full AI app. It isolates the evaluation mechanics. In a real service, app(case, mode) would call your RAG pipeline, tool router, support assistant, refund classifier, or coding agent. The important part is that outputs are scored against examples with known expectations.

Step 3: Verify the Baseline

Run:

.venv/bin/python eval_guardrail.py baseline

The baseline output scored perfectly:

{
  "mode": "baseline",
  "average": 1.0,
  "rows": [
    {
      "id": "refund-status",
      "exact": 1.0,
      "valid_json": 1.0,
      "combined": 1.0
    },
    {
      "id": "shipping-status",
      "exact": 1.0,
      "valid_json": 1.0,
      "combined": 1.0
    }
  ]
}

That is the minimum standard for adding the gate to CI. If your baseline does not pass locally, do not add it to pull requests yet. Fix the dataset, scoring function, prompt, or application behavior first.

Step 4: Trigger a Real Regression

Run:

.venv/bin/python eval_guardrail.py regression
echo "exit_code=$?"

The regression output scored 0.75 and exited with code 1:

{
  "mode": "regression",
  "average": 0.75,
  "rows": [
    {
      "id": "refund-status",
      "exact": 1.0,
      "valid_json": 1.0,
      "combined": 1.0
    },
    {
      "id": "shipping-status",
      "exact": 0.0,
      "valid_json": 1.0,
      "combined": 0.5
    }
  ]
}

This is the useful failure. The model output remained machine-readable, so a schema-only check would have missed it. ExactMatch caught the changed action. In production, this kind of example belongs in an eval suite whenever the output drives workflow state, customer communication, billing, refunds, compliance, or operational escalation.

Step 5: Add the CI Gate

A minimal GitHub Actions job can run the same command:

name: llm-eval

on:
  pull_request:
  push:
    branches: [main]

jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - run: pip install -r requirements.txt
      - run: python eval_guardrail.py baseline

The PoC did not execute this workflow on GitHub. It only validated the local command the workflow would run. For a real repository, add the workflow after the local eval is stable, keep provider keys in GitHub Actions secrets, and avoid running expensive model-backed evals on every trivial documentation change.

How to Evolve This Into a Real Braintrust Setup

The sandbox used Autoevals locally, but Braintrust’s larger value appears when evaluations become experiments and traces rather than isolated scripts. The official docs describe a cycle of playground iteration, experiment promotion, CI/CD automation, production scoring, and feedback into datasets.

A production version should add:

  • A dataset with real failure cases from production or support review.
  • Multiple scorers: schema validity, exact action labels, semantic similarity, retrieval groundedness, and policy checks.
  • Hosted experiment logging through the Braintrust SDK.
  • Baseline comparison using Braintrust experiment history.
  • Pull-request reporting so reviewers see which cases changed.
  • Online scoring rules for production traces.
  • Human review for examples where the correct answer is not purely mechanical.

The Python SDK reference includes optional base experiment fields, which are designed for comparing new experiment runs against earlier runs. That is where a CI gate becomes more useful than a single threshold: it can fail because a change regressed against an accepted baseline, not because a hard-coded number was guessed months ago.

Pricing and Plan Reality

As of the official pricing page checked on May 20, 2026, Braintrust lists a Starter plan at $0 / month with 1 GB processed data, 10k scores, 14 days retention, and unlimited users, projects, datasets, playgrounds, and experiments. The same page lists Pro at $249 / month, with higher included usage and lower overage rates.

The plans-and-limits page also says Starter has a $0 platform fee, no credit card required, 1 GB processed data per month, 10k scores per month, and 14 days retention. It lists sandbox evals as a Pro/Enterprise feature, so do not assume every hosted feature is available on the free plan.

Those details are current only for the sources checked during this run. Pricing and limits change often; verify Braintrust’s pricing page before committing a team budget.

Common Mistakes

The first mistake is making the eval dataset too broad. A five-example eval that catches the three failures that would hurt users is more useful than a 500-example suite nobody trusts. Start with high-risk cases: wrong action, missing escalation, invalid structured output, unsafe instruction following, or retrieval answers that must cite internal policy.

The second mistake is treating JSON validity as correctness. The sandbox regression proves the problem. Valid JSON can still be the wrong answer.

The third mistake is relying only on LLM-as-judge scoring. LLM judges are useful for open-ended outputs, but deterministic checks are cheaper, faster, and easier to reason about when the expected answer is known.

The fourth mistake is blocking every pull request on a slow model-backed eval. Split your suite. Run fast deterministic gates on every PR, and run larger model-backed or hosted experiment suites on scheduled jobs, risky code paths, or explicit release branches.

The fifth mistake is claiming “tested in production” because an eval passed locally. A local eval is evidence, but it is not production monitoring. Keep the claim boundary honest.

FAQ

Q: Can Autoevals run without a Braintrust API key?

Yes for local deterministic scorers in this sandbox. ExactMatch and ValidJSON ran locally without a Braintrust API key or model provider key. Hosted Braintrust experiment logging and provider-backed LLM judges require additional configuration.

Q: Should an LLM eval fail CI?

It should fail CI when the eval checks behavior that must not regress. Examples include structured workflow actions, safety labels, routing decisions, required citations, and schema contracts. For subjective quality checks, use pull-request reports or warnings until the scorer is stable.

Q: Is exact match too strict for LLM outputs?

Exact match is too strict for open-ended prose, but it is appropriate for constrained outputs such as labels, actions, JSON enum fields, and routing decisions. Pair it with schema and semantic scorers when the answer space is flexible.

Q: Does this replace observability?

No. Offline evals catch known cases before deployment. Observability catches real user traces after deployment. A healthy LLM engineering loop uses both.

Q: What did Effloow Lab actually prove?

Effloow Lab proved that the pinned Autoevals package can run a local deterministic scoring gate, that a passing baseline exits successfully, and that a subtle behavior regression exits nonzero. It did not prove hosted Braintrust reporting, model-provider integration, or production monitoring.

Key Takeaways

Braintrust Autoevals is useful even before a full observability rollout. The low-friction starting point is a local eval script that turns critical examples into a CI gate.

The sandbox result is narrow but practical: ValidJSON caught shape, ExactMatch caught behavior, and the process exit code made the regression enforceable. That is enough to justify a first eval suite for any LLM feature that returns structured decisions.

For production, add hosted Braintrust experiment tracking, real provider calls, baseline comparison, and production trace feedback. But do not wait for the perfect eval platform rollout before writing the first regression test. Start with the smallest failure that would embarrass your application, then make that failure impossible to merge quietly.

Sources

How I registered an MCP server for 3,760 retailers — and what I learned

Yesterday CLI Market was a PyPI package.
This week it’s an official MCP Registry server:

Here’s what it took to get listed, what the registry validation looks like, and why MCP is the missing layer between e-commerce and AI agents.

**

The MCP Registry: what it took to get listed

**
The Model Context Protocol Registry at registry.modelcontextprotocol.io is the canonical
directory of MCP servers. Getting listed isn’t just about having a working server — the registry validates ownership, checks schema compliance, and verifies that the package actually exists.

*Step 1: The mcp.json file
*

Every MCP server needs a mcp.json at the repo root. Ours:
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json"
,
"name": "io.github.Treevu-ai/cli-market",
"title": "CLI Market",
"version": "1.0.1",
"description": "12 MCP tools for search, compare, purchase across 3,760 retailers in 67 c
ountries",
"repository": {
"url": "https://github.com/Treevu-ai/cli-market-world",
"source": "github"
},
"packages": [{
"registryType": "pypi",
"identifier": "cli-market",
"version": "1.0.17",
"transport": { "type": "stdio" }
}]
}

Key decisions:

  • Name format: io.github.{org}/{project} — reverse-DNS, org-scoped
  • registryType: pypi — CLI Market is a Python package on PyPI
  • transport: stdio — the MCP server communicates over standard input/output
  • version locked — no ranges, exact version matching

*Step 2: Proving ownership
*

The registry doesn’t trust that cli-market on PyPI belongs to Treevu-ai on GitHub. It validates by scanning the PyPI package README for a specific HTML comment:

<!-- mcp-name: io.github.Treevu-ai/cli-market -->

This line must appear in the README that renders on PyPI. The registry fetches the package, parses the README, and checks that the annotation matches the server name. If it doesn’t match — rejected.

We added this to our README, bumped the version to 1.0.17, and uploaded to PyPI. The registry validated it in under a second.

*Step 3: Schema validation
*

The publish endpoint runs the full ServerJSON schema validator. Every field is checked:

description max 100 characters, name must match ^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$,
packages[].identifier must resolve in the registry, packages[].transport.type must be stdio/
streamable-http/sse.

One thing that tripped us up: the schema changed between mcp.json (the repo-side format) and ServerJSON (the registry API format). In the repo, you declare command, args, type. In the registry API, those go inside packages[].transport and packages[] requires registryType + identifier + transport. Getting this right took a few rounds.

*Step 4: Authentication
*

The registry uses GitHub OAuth. We exchanged a GitHub token for a registry JWT:
POST /v0.1/auth/github-at → { registry_token: "eyJ..." }

Then published:
POST /v0.1/publish → { server: {...}, _meta: { status: "active" } }

The token expires, so for updates you re-authenticate. The whole flow is scriptable in under 10 lines of bash.

The 12 tools: architecture

All 12 tools sit on top of a unified VTEX connector that normalizes 3,760 retailer APIs into a single JSON schema.

_Four tools deserve special attention:
_

  • – market_compare — Not a simple price fetch. It first normalizes SKUs across retailers (the same product has different internal IDs at Carrefour Brazil and Sainsbury’s UK), deduplicates, and returns a canonical price set. Three steps under one MCP call.
  • market_checkout — Chains: cart validation → stock verification → payment method resolution → order confirmation. All in one invocation. But it never completes autonomously — V1 requires explicit human approval before execution.
  • market_ask — The most composite tool. Natural language input → semantic search → cross-retailer comparison → cart building → checkout readiness. “Buy rice” walks the full pipeline.

The composition pattern — Tools are atomic enough to compose, composite enough to not drown the agent. 12 tools. Not 1 mega-tool. Not 100 individual REST endpoints.

───

What I’d do differently

  1. Read the OpenAPI spec first. The publish endpoint schema is documented at /docs. I guessed from the mcp.json format and hit 5 validation errors before getting it right.

  2. Set up the PyPI annotation early. The mcp-name comment is the ownership proof. Add it to your README before you publish to the registry.

  3. Script the auth flow. GitHub token → registry JWT → publish. Three curl calls. Wrap them in a Makefile target and forget about it.

───

CLI Market is on the registry.
The agent can now search, compare, and buy across 3,760 retailers in 67 countries — all via structured tool calls with zero scraping.

pip install cli-market
io.github.Treevu-ai/cli-market
github.com/Treevu-ai/cli-market-world

Ricardo Cuba
Founder & Product Lead | CLI Market
CEO Sinapsis Innovadora
Trujillo, Perú