<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>shpapps blog</title><link href="https://shpapps.dev/blog/"/><id>https://shpapps.dev/blog/</id>
<updated>2026-10-18T08:00:00Z</updated><author><name>Francesco Noferi</name></author><entry><title>Recipes, not prompts: one edge in front of every app</title><link href="https://shpapps.dev/blog/recipes-not-prompts/"/><id>https://shpapps.dev/blog/recipes-not-prompts/</id>
<updated>2026-10-18T08:00:00Z</updated><summary>Our apps never name a model or send a prompt. They name a recipe, and a small Cloudflare Worker does the rest. Here is the pattern and why it has paid for itself.</summary></entry><entry><title>Short, springy, about the thing touched: RetroReady&#x27;s motion rules</title><link href="https://shpapps.dev/blog/short-springy-motion/"/><id>https://shpapps.dev/blog/short-springy-motion/</id>
<updated>2026-10-18T08:00:00Z</updated><summary>The four animation pieces behind RetroReady, with their real numbers in Flutter and CSS, and the rule that decided all of them.</summary></entry><entry><title>Valid JSON, junk inside: guarding model output before users see it</title><link href="https://shpapps.dev/blog/valid-json-junk-inside/"/><id>https://shpapps.dev/blog/valid-json-junk-inside/</id>
<updated>2026-10-18T08:00:00Z</updated><summary>Structured output mode guarantees JSON, not good JSON. A cheap second check against the schema and a short list of junk patterns turned unusable answers into failed steps the ladder could route around.</summary></entry></feed>
