<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Nate Swenson — Dev Log</title><description>The dev log of Nate Swenson, Senior DevOps Engineer at GoodLeap. Daily notes on the projects he&apos;s building in public — shipped features, the tradeoffs behind them, and what broke.</description><link>https://natejswenson.com/</link><language>en-us</language><item><title>Teaching a writing skill to cite its work, and verifying it did</title><link>https://natejswenson.com/devlog/devlog/v0.4.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/devlog/v0.4.0/</guid><description>devlog 0.4.0 makes /devlog write researched, cited implementation guides instead of changelog summaries. The hard part is not the prompt; it is that language models fabricate citations at high rates, so the real work is a citation floor and a structural contract you verify in code rather than hope the model honors.</description><pubDate>Sun, 28 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;devlog 0.4.0 changes what &lt;code&gt;/devlog&lt;/code&gt; writes. Each release entry used to be a narrative summary of what shipped. Now it is a researched, end-to-end implementation guide: a short “what shipped” hook, then a deep dive that teaches the engineering topic the work touched, with copy-paste code, a &lt;code&gt;## Sources&lt;/code&gt; section, and inline citations. A new &lt;code&gt;deepDive&lt;/code&gt; config block sets the topic domains the post may explore and a &lt;code&gt;minSources&lt;/code&gt; floor for how many reputable sources it must cite.&lt;/p&gt;
&lt;p&gt;The instructions are the easy half. The half worth writing about is trust. The moment you ask a model to “cite reputable sources,” you have to assume it will sometimes invent them, so the useful work is encoding a citation floor and a structure as a contract, then checking the output against that contract instead of trusting the prose.&lt;/p&gt;
&lt;h2&gt;Why “cite your sources” is not enough&lt;/h2&gt;
&lt;p&gt;A skill is a Markdown spec the model reads and follows; Anthropic’s own Agent Skills are folders whose &lt;code&gt;SKILL.md&lt;/code&gt; is deliberately both machine-parseable and human-readable (&lt;a href=&quot;https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills&quot;&gt;Anthropic: Equipping agents with Agent Skills&lt;/a&gt;). That makes the behavior easy to change and impossible to guarantee. An instruction is a strong suggestion, not an enforced invariant.&lt;/p&gt;
&lt;p&gt;Citations are exactly where that gap bites. Large-scale analysis of references in published papers found fabricated or invalid citations rising sharply as model-assisted writing spread (&lt;a href=&quot;https://arxiv.org/abs/2602.06718&quot;&gt;GhostCite: citation validity in the age of LLMs&lt;/a&gt;), and models are often unable to tell when a reference they produced is real (&lt;a href=&quot;https://arxiv.org/abs/2305.18248&quot;&gt;Do Language Models Know When They’re Hallucinating References?&lt;/a&gt;). A confident, well-formatted citation to a paper or doc that does not exist is the default failure mode, not an edge case. So a post that merely &lt;em&gt;asks&lt;/em&gt; for sources can read as rigorous while linking to nothing real.&lt;/p&gt;
&lt;p&gt;The fix is the same discipline Anthropic recommends for agents generally: define what good looks like, then measure it in the loop rather than eyeballing the output once (&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt;). For a writing skill, “what good looks like” is a structural contract, and “measure it” is a linter you can run on every generated post.&lt;/p&gt;
&lt;h2&gt;Setup: declare the contract as config&lt;/h2&gt;
&lt;p&gt;Start by making the floor data, not prose. The release added a &lt;code&gt;deepDive&lt;/code&gt; block so the citation minimum and the allowed topic domains live in config, where they can be read by both the skill and a checker:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;deepDive&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    &quot;topicDomains&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;AI&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;DevOps/SRE&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;software engineering&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    &quot;minSources&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The contract itself is small and explicit: every post must have a &lt;code&gt;## Shipped&lt;/code&gt; hook, at least one language-tagged code block, a &lt;code&gt;## Sources&lt;/code&gt; section carrying at least &lt;code&gt;minSources&lt;/code&gt; resolvable links, and a &lt;code&gt;## Changelog&lt;/code&gt;. Those are the load-bearing parts a reader relies on, so they are the parts worth enforcing.&lt;/p&gt;
&lt;h2&gt;Build: lint the post against the contract&lt;/h2&gt;
&lt;p&gt;The checker reads a finished post and returns a list of problems. It is deliberately boring: parse the text, look for the required sections, count the citations under &lt;code&gt;## Sources&lt;/code&gt;, and confirm at least one real code block. Boring is the point, because a deterministic check cannot be talked out of failing the way a model can.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# lint_post.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re, sys, pathlib&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;REQUIRED &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Shipped&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Sources&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Changelog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;LINK &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.compile(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&quot;\[[^\]]+\]\((https?://[^&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;]+)\)&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;CODE_BLOCK &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.compile(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&quot;^```[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;a-z&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;]+\n.*?\n```&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, re.S &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.M)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; section&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(text: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, heading: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Return the body under a real `## heading` LINE, up to the next `## `.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    Anchoring on a line start matters: the post mentions `## Sources` inline in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    its own prose, and a naive text.split() would match that, not the section.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    start &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.search(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;rf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;(?m)^## &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;re.escape(heading)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;\s*$&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, text)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; start:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    rest &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; text[start.end():]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    nxt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.search(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&quot;(?m)^## &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, rest)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; rest[: nxt.start()] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; nxt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; rest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; lint_post&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(path: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, min_sources: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib.Path(path).read_text(encoding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    problems &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;missing section: ## &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;h&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; h &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; REQUIRED &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; section(text, h)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; CODE_BLOCK.search(text):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        problems.append(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;no language-tagged code block&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    cited &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; LINK.findall(section(text, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Sources&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(cited) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; min_sources:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        problems.append(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(cited)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; cited sources, need &gt;= &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;min_sources&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; problems&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Verify: resolve the links, do not just count them&lt;/h2&gt;
&lt;p&gt;Counting links is not enough, because a fabricated citation is still a syntactically valid Markdown link. The check that actually catches hallucinated references is resolving each URL, since a made-up source usually fails to resolve or 404s:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# check_links.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; urllib.request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; dead_links&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(urls: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], timeout: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 10.0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;A fabricated citation can still look like a valid link. Resolve each one;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    report the URLs that do not come back.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    dead &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; url &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; urls:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        req &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; urllib.request.Request(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            url, method&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;HEAD&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, headers&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;User-Agent&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;devlog-linter&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        try&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; urllib.request.urlopen(req, timeout&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;timeout) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.status &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 400&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                    dead.append(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -&gt; HTTP &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;resp.status&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        except&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Exception&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; exc:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            dead.append(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{type&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(exc).__name__&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dead&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then wire both into the pipeline so no post ships unchecked. Running it on a draft prints the contract violations and stops the build on any of them:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; python&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; lint_post.py&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; devlog/v0.4.0.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; cited&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; sources,&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; need&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt; # fix the post, then it exits clean and the link resolver runs in CI&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A HEAD request is best-effort, not proof: a live URL can still point at the wrong content, and a flaky host can fail a real link, so treat resolution as a strong signal you review, not an oracle. It catches the common, embarrassing case, which is a link to something that was never there. The deeper lesson is the one the eval guidance keeps making: the model is a generator, and the trustworthy part of the system is the check you run on what it generated, not the generation itself.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The contract lives in the skill’s instructions today, and the linter is the deterministic backstop. The natural next step is to run that linter as a gate in the publishing path, so a post that drops below the citation floor or loses a section cannot be pushed, the same way tests gate code.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills&quot;&gt;Anthropic: Equipping agents for the real world with Agent Skills&lt;/a&gt; — a skill is a Markdown &lt;code&gt;SKILL.md&lt;/code&gt; spec, both machine-parseable and human-readable.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2602.06718&quot;&gt;GhostCite: A Large-Scale Analysis of Citation Validity in the Age of Large Language Models&lt;/a&gt; — fabricated and invalid citations are common and rising in model-assisted writing.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2305.18248&quot;&gt;Do Language Models Know When They’re Hallucinating References?&lt;/a&gt; — models frequently cannot tell whether a reference they produced is real.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt; — define success, then measure it in the loop rather than eyeballing output.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat(devlog): researched, end-to-end implementation-guide posts (0.4.0) (&lt;a href=&quot;https://github.com/natejswenson/claude-skills/commit/544755ba8c8bec11b30523f3c86bd6fd58cea9eb&quot;&gt;544755b&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Giving a solo project a real branch model (and why it isn&apos;t overkill)</title><link>https://natejswenson.com/devlog/local-fitness/v0.12.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/local-fitness/v0.12.0/</guid><description>Why a one-person, agent-built project still earns a feature→dev→main branch model with automatic release tagging, the CI wiring that cuts the tag only when the version is new, and the dependency drift a single major bump surfaced.</description><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;v0.12.0 gave the fitness agent’s release process an actual shape. Work now lands on a feature branch, merges into &lt;code&gt;dev&lt;/code&gt;, promotes to &lt;code&gt;main&lt;/code&gt;, and the release tag is cut automatically when it gets there. I shipped the wiring first and left branch protection for next. Alongside it I cleared a backlog of dependency bumps, including moving the web builder from Node 22 to Node 26.&lt;/p&gt;
&lt;p&gt;That’s the changelog. The part worth writing about is the actual wiring: what the research says about which branch model to pick, the CI that cuts a trustworthy tag without me touching it, and the dependency drift the sweep surfaced when a single major bump tripped an assumption. Here is the end-to-end build.&lt;/p&gt;
&lt;h2&gt;Setup: the branch model, and why a solo repo earns it&lt;/h2&gt;
&lt;p&gt;For a long time my honest answer was that a solo project needs no branch model at all. When you’re the only committer, committing straight to &lt;code&gt;main&lt;/code&gt; is fine, and the branching is ceremony you pay for and never use. What changed wasn’t team size, it was who’s committing: an agent now opens PRs against this repo, a dependency bot bumps versions on its own schedule, and releases have to be repeatable.&lt;/p&gt;
&lt;p&gt;It helps to anchor this in evidence rather than taste. DORA’s research on trunk-based development found that teams perform better on software delivery when they keep three or fewer active branches, merge to the mainline at least once a day, and avoid long code-freeze or integration phases (&lt;a href=&quot;https://dora.dev/capabilities/trunk-based-development/&quot;&gt;DORA: Trunk-based development&lt;/a&gt;). Short-lived branches drift less from the mainline, so merges stay small and conflicts stay rare. That research is also the reason I did not reach for GitFlow, which keeps a long-lived &lt;code&gt;develop&lt;/code&gt; branch plus temporary &lt;code&gt;release&lt;/code&gt; and &lt;code&gt;hotfix&lt;/code&gt; supporting branches, built around scheduled, versioned releases with a separate stabilization phase (&lt;a href=&quot;https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow&quot;&gt;Atlassian: Gitflow workflow&lt;/a&gt;). That solves a problem a continuously-shipped side project does not have, so most of its machinery would sit unused while still charging me the overhead.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;feature → dev → main&lt;/code&gt; is the middle, and it keeps the trunk-based property that matters: branches stay short and merge often. &lt;code&gt;dev&lt;/code&gt; is the integration line that stays releasable, &lt;code&gt;main&lt;/code&gt; is the line that gets tagged and deployed. The prerequisite is just two long-lived branches and the discipline that day-to-day work always starts from &lt;code&gt;dev&lt;/code&gt;, never from &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# main already exists and is the deploy line. Add the integration line.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; switch&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; dev&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; push&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -u&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; origin&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# every change, mine or the agent&apos;s, starts from dev and never from main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; switch&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; feature/auto-tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I turned this on as plumbing first, deliberately before branch protection. Protection rules reference branches and required status checks, so switching them on before those branches and checks have a track record mostly just blocks you. DORA’s own writeup names an overly heavy review-and-approval gate as a common obstacle to actually adopting trunk-based development (&lt;a href=&quot;https://dora.dev/capabilities/trunk-based-development/&quot;&gt;DORA: Trunk-based development&lt;/a&gt;), which is the same failure in a different costume. The safer order is to create the branches, wire the flow, get CI green, watch one real release move all the way through, and only then add the rules on top of a path you’ve already seen work.&lt;/p&gt;
&lt;h2&gt;Build: CI on every PR into dev and main&lt;/h2&gt;
&lt;p&gt;The flow only earns its keep if something checks the integrated change before it advances. One workflow, triggered on pull requests into either line, builds and tests the web app. The same job runs whether you’re merging a feature into &lt;code&gt;dev&lt;/code&gt; or promoting &lt;code&gt;dev&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt;, so nothing reaches the deploy line without having passed on the integration line first.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# .github/workflows/ci.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name: ci&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;on:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  pull_request:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    branches: [dev, main]   # runs on PRs into either line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;jobs:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  build-test:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    runs-on: ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    steps:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - uses: actions/checkout@v7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - uses: actions/setup-node@v6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        with:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          node-version: 26&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - name: build and test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        working-directory: web&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        run: |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          npm ci&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          npm run build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          npm test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build: cut the release tag automatically, but only when the version is new&lt;/h2&gt;
&lt;p&gt;The release tag is cut automatically when a change reaches &lt;code&gt;main&lt;/code&gt;. Automating it isn’t about saving a command. Semantic versioning only works as a communication tool if the number is trustworthy; the spec exists so a version conveys real meaning about what changed (&lt;a href=&quot;https://semver.org/&quot;&gt;Semantic Versioning 2.0.0&lt;/a&gt;). A manually applied tag is a step you eventually forget or apply inconsistently, and the moment that happens the version starts lying about what’s deployed.&lt;/p&gt;
&lt;p&gt;A separate workflow runs on pushes to &lt;code&gt;main&lt;/code&gt;, reads the version out of &lt;code&gt;package.json&lt;/code&gt;, and tags only if no tag for that version exists yet. That guard is the whole point: every merge into &lt;code&gt;main&lt;/code&gt; fires the job, but most merges do not bump the version, so the job has to be a no-op unless the number actually changed. Tying the tag to “this version reached &lt;code&gt;main&lt;/code&gt; for the first time” collapses it to a single meaning, which is what lets the changelog, this dev log, and a future deploy all key off it without re-deriving whether something is really released.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# .github/workflows/release.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name: release&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;on:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  push:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    branches: [main]   # fires on every merge into the deploy line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;jobs:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  tag:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    runs-on: ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    permissions:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      contents: write   # required to push the tag back&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    steps:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - uses: actions/checkout@v7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        with:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          fetch-depth: 0   # full history so existing tags are visible&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - name: read version from package.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        id: ver&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        run: echo &quot;version=$(node -p &quot;require(&apos;./web/package.json&apos;).version&quot;)&quot; &gt;&gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - name: skip if this version is already tagged&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        id: check&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        run: |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          # -q --verify against refs/tags so only a real tag counts, not any ref&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          if git rev-parse -q --verify &quot;refs/tags/v${{ steps.ver.outputs.version }}&quot; &gt;/dev/null; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            echo &quot;exists=true&quot;  &gt;&gt; &quot;$GITHUB_OUTPUT&quot;   # version unchanged, do nothing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            echo &quot;exists=false&quot; &gt;&gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - name: cut the release tag&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        if: steps.check.outputs.exists == &apos;false&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        run: |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          # an annotated tag needs a tagger identity; the runner has none by default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          git config user.name &quot;github-actions[bot]&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          git config user.email &quot;41898282+github-actions[bot]@users.noreply.github.com&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          git tag -a &quot;v${{ steps.ver.outputs.version }}&quot; -m &quot;Release v${{ steps.ver.outputs.version }}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          git push origin &quot;v${{ steps.ver.outputs.version }}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build: do dependency work in small sweeps&lt;/h2&gt;
&lt;p&gt;The same release cleared a stack of bumps: react and its types, react-router-dom, lucide-react, vite and the vite react plugin, the &lt;code&gt;setup-uv&lt;/code&gt; and &lt;code&gt;checkout&lt;/code&gt; CI actions, and the web builder’s base image from Node 22 to Node 26. None of it is user-facing, which is exactly why it had been sitting untouched.&lt;/p&gt;
&lt;p&gt;The major one, Node 22 to 26, is the kind of bump that makes the case for small regular sweeps over one heroic upgrade. Drift is invisible right up until a major version trips an assumption you didn’t know you’d made, and untangling several of those at once costs far more than handling one at a time. The Node jump is also a good reminder to check what changed at the runtime level: Corepack stopped shipping with Node as of 25 (&lt;a href=&quot;https://socket.dev/blog/node-js-tsc-votes-to-stop-distributing-corepack&quot;&gt;Socket: Node.js TSC votes to stop distributing Corepack&lt;/a&gt;; &lt;a href=&quot;https://github.com/nodejs/corepack&quot;&gt;nodejs/corepack&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;One commit in the sweep installs Corepack explicitly in the build image. Because Corepack no longer ships with Node, enabling it now takes two steps: install it from npm first, then turn it on.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# web/Dockerfile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; node:26-bookworm-slim&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Corepack isn&apos;t bundled as of Node 25, so install it before enabling&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; npm i -g corepack &amp;#x26;&amp;#x26; corepack enable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That change is worth being honest about: it only matters if you pin pnpm or yarn through &lt;code&gt;package.json&lt;/code&gt;’s &lt;code&gt;packageManager&lt;/code&gt; field, since Corepack is what shims those. This project builds with npm, and npm still ships with Node 26, so the line was belt-and-suspenders rather than the thing the bump actually required. If you do use pnpm or yarn, that same &lt;code&gt;npm i -g corepack &amp;amp;&amp;amp; corepack enable&lt;/code&gt; (in the Dockerfile, and in the CI job above) is the substitution to make.&lt;/p&gt;
&lt;h2&gt;Use it: how a change flows through&lt;/h2&gt;
&lt;p&gt;With both workflows in place, the path is mechanical. A change starts on a feature branch and its PR targets &lt;code&gt;dev&lt;/code&gt;, never &lt;code&gt;main&lt;/code&gt;. Once &lt;code&gt;dev&lt;/code&gt; is green and I want to ship, a second PR promotes &lt;code&gt;dev&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt;, and merging that one is what fires the release workflow.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 1. feature work lands via a PR into dev; ci.yml runs on it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;gh&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; pr&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --base&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; dev&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --head&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; feature/auto-tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --fill&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 2. to ship, promote the integration line to the deploy line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;gh&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; pr&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --base&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --head&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; dev&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --title&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;release: v0.12.0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# merging the promotion PR pushes main, which triggers release.yml.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# release.yml reads the bumped version and cuts v0.12.0 because no tag exists yet.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A version bump on &lt;code&gt;dev&lt;/code&gt; only becomes a real release at the moment it crosses into &lt;code&gt;main&lt;/code&gt;. Nothing gets tagged speculatively from a feature branch, and a promotion that doesn’t change the version simply re-runs the guard and exits.&lt;/p&gt;
&lt;h2&gt;Verify: confirm the tag matches the shipped version&lt;/h2&gt;
&lt;p&gt;The whole point of automating the tag was to keep the version honest, so the check that proves it is a one-liner. After the promotion merges, fetch tags and assert that the latest tag matches the version in &lt;code&gt;package.json&lt;/code&gt;. If they ever disagree, either the bump never happened or the release workflow didn’t run, and both are worth knowing immediately.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --tags&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;expected&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;v$(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -p &quot;require(&apos;./web/package.json&apos;).version&quot;)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# describe returns the nearest tag by history, not the highest semver; right&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# after a promotion that is the tag just cut. To assert on exactly the merge&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# commit instead, use: actual=&quot;$(git tag --points-at HEAD)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the 2&gt;/dev/null || echo none fallback matters on a very first release, where&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# HEAD has no reachable tag yet and bare describe would exit non-zero and abort&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;actual&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;$(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; describe --tags --abbrev=0 &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;2&gt;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/dev/null &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; none)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$expected&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$actual&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ]; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;release tag &lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$actual&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; matches package version&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;MISMATCH: package says &lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$expected&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; but latest tag is &lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$actual&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&amp;#x26;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  exit&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The guard inside the workflow handles the other direction: re-running a merge that didn’t bump the version finds the tag already present and skips, so a no-op promotion can never produce a duplicate or a misleading tag. Drop that assertion into CI later and the version can’t quietly start lying again.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;Branch protection on &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;main&lt;/code&gt; is the follow-up. The branches exist and have moved a real release end to end now, so the rules finally have something solid to attach to.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dora.dev/capabilities/trunk-based-development/&quot;&gt;DORA: Trunk-based development&lt;/a&gt; — research linking few short-lived branches and daily merges to higher delivery performance, and naming heavy approval gates as an adoption obstacle.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow&quot;&gt;Atlassian: Gitflow workflow&lt;/a&gt; — what GitFlow’s long-lived branches are built for.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://semver.org/&quot;&gt;Semantic Versioning 2.0.0&lt;/a&gt; — why a version number has to be trustworthy to mean anything.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://socket.dev/blog/node-js-tsc-votes-to-stop-distributing-corepack&quot;&gt;Socket: Node.js TSC votes to stop distributing Corepack&lt;/a&gt; and &lt;a href=&quot;https://github.com/nodejs/corepack&quot;&gt;nodejs/corepack&lt;/a&gt; — Corepack no longer bundled as of Node 25.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;chore: release 0.12.0, feature-&amp;gt;dev-&amp;gt;main branch model + auto-tag (#36) (#37) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/94426f54797c9984cb4725b6d61c5d69c64b3780&quot;&gt;94426f5&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;fix: install corepack explicitly for node:26 web-builder (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/1e4e80d7076e725c626f797c32ad2602cd348a99&quot;&gt;1e4e80d&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;ci: adopt feature-&amp;gt;dev-&amp;gt;main branch model (plumbing, pre-protection) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/df36aa658bcdbf91e25d3a74182ee74dae76d42b&quot;&gt;df36aa6&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;chore(deps): bump react-router-dom, lucide-react, vite in /web (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/9255a5244e8d95736ca39d4773136a0a251c664e&quot;&gt;9255a52&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;chore(deps): bump react and @types/react in /web (#15) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/d8e6a97452da3be05d4bbbb7efe4e14306c46a86&quot;&gt;d8e6a97&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;chore(deps): bump node from 22-bookworm-slim to 26-bookworm-slim (#29) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/65029567f27bde1d04db22c0ac2c6479850ee467&quot;&gt;6502956&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;chore(deps): bump actions/checkout from 4 to 7 (#30) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/9e1c09be899ce0ec959886f2740e69b631c1c6f2&quot;&gt;9e1c09b&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>A new card type, and the case for growing a vocabulary over options</title><link>https://natejswenson.com/devlog/ghostwriter/v0.5.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/ghostwriter/v0.5.0/</guid><description>Ghostwriter got a matrix comparison card. The more interesting part is the rule behind it: when a post shape recurs it earns its own card type, instead of collapsing everything into one configurable mega-card.</description><pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;Ghostwriter builds the visual cards that ship alongside a LinkedIn post, and v0.5.0 adds a matrix card: a comparison grid of rows and columns for posts whose point is a few options weighed against each other. It joins the existing family of ramp, flow, terminal, and carousel cards.&lt;/p&gt;
&lt;p&gt;The card itself is small. The part worth writing about is the system it slots into and the rule that governs it. Each card type is a tiny, self-contained thing, and a new shape only becomes a type when it recurs, instead of every shape collapsing into one configurable mega-card. Here is the full path: how a type is declared and picked, how I added the matrix type, how it renders a real comparison, and the concrete bar a new type has to clear before it earns a place.&lt;/p&gt;
&lt;h2&gt;How a card type is declared and picked&lt;/h2&gt;
&lt;p&gt;A card type in Ghostwriter is not a class with a &lt;code&gt;type&lt;/code&gt; flag. It is three small artifacts that share a name: an HTML template (&lt;code&gt;assets/card-template-&amp;lt;type&amp;gt;.html&lt;/code&gt;), a block of scoped CSS (&lt;code&gt;.card.&amp;lt;type&amp;gt;&lt;/code&gt; rules in &lt;code&gt;assets/diagram.css&lt;/code&gt;), and a guidance entry in &lt;code&gt;SKILL.md&lt;/code&gt; that tells the generator when to reach for it. The renderer, &lt;code&gt;scripts/render_image.py&lt;/code&gt;, does not know any type names; it inlines the CSS into whatever HTML you hand it and screenshots the &lt;code&gt;#canvas&lt;/code&gt; element. That is the whole registry, and it is deliberately file-based rather than coded.&lt;/p&gt;
&lt;p&gt;This is the AHA instinct made structural, “Avoid Hasty Abstractions,” preferring a little duplication over a shared component that has to know about every shape at once (&lt;a href=&quot;https://kentcdodds.com/blog/aha-programming&quot;&gt;AHA Programming&lt;/a&gt;). Adding a type is adding a file and a CSS block, not editing a switch that every other type also runs through. The template is the declaration: it states the type’s shape and its contract in one place.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;&amp;#x3C;!-- assets/card-template-matrix.html — the matrix type&apos;s declaration --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;!&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;doctype&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; html&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; lang&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; charset&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;diagram.css&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;canvas&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card matrix&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;toprow&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;eyebrow&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;EYEBROW LABEL&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;footer brand&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt; &amp;#x3C;!-- byline; a feed crop can&apos;t remove it --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;One sharp headline.&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;sub&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;A single supporting line, if needed.&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    &amp;#x3C;!-- columns are set per-instance: 1.3fr label gutter + one 1fr per option --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;grid&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;grid-template-columns: 1.3fr repeat(3, 1fr)&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;                       &amp;#x3C;!-- empty corner above the row gutter --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;col-h green&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Option A&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;col-h grey&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Option B&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;col-h pink&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Option C&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;switch&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;a number group&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  &amp;#x3C;!-- spans all columns as a divider --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;dial&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Row label&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;9&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  &amp;#x3C;!-- .v = big mono number --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;switch&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;a plain-english group&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;dial&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Behaves&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;vt&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;never&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;vt&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;on a miss&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;vt&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;below goal&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  &amp;#x3C;!-- .vt = short phrase --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The contract is small and enforced by the class names: header cells (&lt;code&gt;.col-h&lt;/code&gt;) carry a column accent, a value cell is either a number (&lt;code&gt;.v&lt;/code&gt;) or a short phrase (&lt;code&gt;.vt&lt;/code&gt;), and a &lt;code&gt;.switch&lt;/code&gt; row groups the rows below it. The &lt;code&gt;#canvas&lt;/code&gt; id and the &lt;code&gt;diagram.css&lt;/code&gt; link are the only things the renderer requires, which is why every type can be a flat file.&lt;/p&gt;
&lt;h2&gt;Adding the matrix type&lt;/h2&gt;
&lt;p&gt;Most of the type lives in CSS, because the template only names structure and the look comes from the shared brand guide. The matrix rules turn that flat list of cells into an aligned grid: the first column is a label gutter, then one equal column per option, with a monospace number treatment for &lt;code&gt;.v&lt;/code&gt; cells and a smaller sans treatment for &lt;code&gt;.vt&lt;/code&gt; phrases. Keeping all of it under &lt;code&gt;.card.matrix&lt;/code&gt; means it cannot leak into any other type.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* assets/diagram.css — shared brand tokens, defined once and used by every card type.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   (Values shown are the project defaults; swap them for your own brand guide.) */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:root&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;Inter&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;system-ui&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;sans-serif&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --mono&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;JetBrains Mono&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;ui-monospace&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;monospace&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --accent&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;   #58A6FF&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* group-divider (.switch) labels */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --accent-2&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #3FB950&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* the &quot;green&quot; column accent */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --accent-3&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #F778BA&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* the &quot;pink&quot; column accent */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --muted&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    #8B949E&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* row labels */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* the base card every type inherits: a fixed 1280x1280 dark canvas.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   This is what makes the light text colours below legible; render the same&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   markup on a white, content-sized box and you get near-white text on white. */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  width&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1280px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; height&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1280px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  box-sizing&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; border-box&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  padding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 72px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  background&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #0D1117&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #E6EDF3&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  font-family&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* the matrix type&apos;s rendering, scoped to .card.matrix */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card.matrix&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; flex-direction&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; column&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card.matrix&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; h1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 800&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 78px&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1.04&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #E6EDF3&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; margin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 22px&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 8px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card.matrix&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; .grid&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; grid&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  /* no fixed track count here: the template sets grid-template-columns inline,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;     one 1fr per option, so a 2-option card has no phantom empty column */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  row-gap&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 24px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  margin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; auto&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;                           &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* vertically center the table */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card.matrix&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; .col-h&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 700&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 50px&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; text-align&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card.matrix&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; .green&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--accent-2&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card.matrix&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; .grey&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  { color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #C9D1D9&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card.matrix&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; .pink&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  { color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--accent-3&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card.matrix&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; .dial&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  { font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 500&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 40px&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--muted&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card.matrix&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; .v&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  { font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 700&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 64px&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--mono&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #E6EDF3&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; text-align&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; } &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* number */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card.matrix&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; .vt&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 500&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 33px&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1.18&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #C9D1D9&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; text-align&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; } &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* phrase */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card.matrix&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; .switch&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {                       &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* full-width group divider */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  grid-column&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; / &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;-1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 600&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 26px&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--mono&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--accent&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  letter-spacing&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0.07em&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; text-transform&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; uppercase&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The third artifact is the guidance entry, and it is the one that does the picking. The generator here is an LLM-driven skill that reads &lt;code&gt;SKILL.md&lt;/code&gt; as part of its prompt, so it chooses a type from these descriptions rather than from any selection code; if you wire this registry into a generator that is not LLM-driven, you inherit the file-based registry and wiring discipline but supply your own selection logic. Because the model is doing the choosing, the entry has to say what the type is for and where its edges are. The edges matter here because not every “this versus that” deserves a grid. The Nielsen Norman Group’s guidance is specific: comparison tables earn their place when someone is deciding between a small number of items across multiple attributes (&lt;a href=&quot;https://www.nngroup.com/articles/comparison-tables/&quot;&gt;NN/g: Comparison Tables&lt;/a&gt;). One attribute is a sentence; a dozen options is a wall. The guidance encodes that narrow middle so the model reaches for the matrix only when the shape fits.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- `assets/card-template-matrix.html` — **matrix type** (a comparison): a labeled grid&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  that compares a few options (columns) across the same attributes (rows). Header cells&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  (`.col-h`) carry a column accent; value cells are `.v` for a big mono NUMBER or `.vt`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  for a short plain-English phrase; `.switch` rows group rows. Keep it to &amp;#x3C;=4 columns and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;=7 rows so it stays scannable at phone size. Reach for it ONLY when a reader is choosing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  between a handful of options on several attributes that genuinely differ; if there&apos;s one&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  attribute, write a sentence, and if there are a dozen options, a grid buries them.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Rendering a real comparison&lt;/h2&gt;
&lt;p&gt;To use the type, copy the template to &lt;code&gt;images/&amp;lt;slug&amp;gt;.html&lt;/code&gt;, fill in the real options and attributes, and render. The renderer inlines &lt;code&gt;diagram.css&lt;/code&gt; so the output is path-independent, then screenshots &lt;code&gt;#canvas&lt;/code&gt; at 2x into a PNG. &lt;code&gt;scripts/render_image.py&lt;/code&gt; is a thin Playwright wrapper; the part that matters is these few lines:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# scripts/render_image.py (essence): inline the CSS, crop to #canvas, save at 2x.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; playwright.sync_api &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; render&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(html_path: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, out_path: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # inline the shared stylesheet so the page needs no web server or sibling files;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # the template&apos;s relative &amp;#x3C;link href=&quot;diagram.css&quot;&gt; would otherwise dangle.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    css &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;assets/diagram.css&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).read_text()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    html &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(html_path).read_text()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # match the link tag in any form (attribute order, quoting, self-closing slash)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # rather than a byte-exact string that would silently no-op on the smallest drift.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    html &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.sub(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&apos;&amp;#x3C;link[^&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;]+diagram\.css[^&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;]*&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&amp;#x3C;style&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;css&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&amp;#x3C;/style&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, html)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;diagram.css&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; html, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;stylesheet link was not inlined; card would be unstyled&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p.chromium.launch() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; browser:                  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# closed on block exit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            page &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; browser.new_page(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                viewport&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1280&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1280&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                device_scale_factor&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,                        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 2x = crisp PNG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            page.set_content(html)                            &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# inlined CSS, no paths to resolve&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            page.locator(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;#canvas&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).screenshot(path&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;out_path)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# crop to the card&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;rendered &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;out_path&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __name__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sys&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    render(sys.argv[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], sys.argv[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;])                          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# render &amp;#x3C;in.html&gt; &amp;#x3C;out.png&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that in place, rendering a real card is a one-time environment setup, then a copy, an edit, and one command:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# one-time setup: create the venv and install Playwright + its Chromium build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;python&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; venv&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; .venv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.venv/bin/pip&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; playwright&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.venv/bin/playwright&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; chromium&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# copy the type&apos;s template, fill it with a real comparison, render to PNG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;cp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; assets/card-template-matrix.html&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; images/card-vocabulary.html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ...edit images/card-vocabulary.html: set the headline, columns, and rows...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.venv/bin/python&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; scripts/render_image.py&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  images/card-vocabulary.html&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  images/card-vocabulary.png&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# expected result: a PNG cropped to #canvas at 2x device scale (the script&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# prints &quot;rendered images/card-vocabulary.png&quot; and nothing else)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The filled grid for this very post compares the two ways to grow a card family. The numbers and phrases are the type’s real contract, a &lt;code&gt;.v&lt;/code&gt; cell per count and a &lt;code&gt;.vt&lt;/code&gt; cell per behavior:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;&amp;#x3C;!-- images/card-vocabulary.html — the .grid, filled with a real comparison --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;&amp;#x3C;!-- two options here, so two 1fr tracks; no fixed 4-column rule to fight --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;grid&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;grid-template-columns: 1.3fr repeat(2, 1fr)&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;col-h green&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Vocabulary&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;col-h pink&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Mega-card&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;switch&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;cost to add a shape&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;dial&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Files touched&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;          &amp;#x3C;!-- new file vs edit template+css+switch --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;dial&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;New conditionals&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;switch&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;cost of being wrong&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;dial&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Blast radius&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;vt&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;one type&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;vt&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;every type&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run it, look at the PNG, and iterate on the source until it reads cleanly cold, because a stranger scrolling a feed will not know any insider units.&lt;/p&gt;
&lt;h2&gt;The bar for adding versus merging a type&lt;/h2&gt;
&lt;p&gt;A file-based registry makes adding a type cheap, which is exactly why it needs a bar. The bar is recurrence, not novelty. A shape earns a type once it has shown up enough times to be a real pattern, which is the old rule of three from refactoring practice: let a thing occur about three times before you treat it as a pattern worth extracting (&lt;a href=&quot;https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)&quot;&gt;Rule of three&lt;/a&gt;). Reaching for an abstraction on the first sighting is how you guess wrong about its shape.&lt;/p&gt;
&lt;p&gt;The opposite move is the one that keeps the family honest. The temptation around the fourth type is to fold them all into one mega-card with a flag and a branch per shape, and that is the failure Sandi Metz named: duplication is far cheaper than the wrong abstraction, because the unified component slowly grows a parameter and a conditional for every case until no one can change it safely (&lt;a href=&quot;https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction&quot;&gt;The Wrong Abstraction&lt;/a&gt;). The merge only makes sense in reverse: when two types turn out to render the same structure, collapse them, and not a release sooner.&lt;/p&gt;
&lt;p&gt;That merge condition is the part you can actually approximate. A small test walks the registry and asserts two things: every declared template is fully wired (it has a matching &lt;code&gt;.card.&amp;lt;type&amp;gt;&lt;/code&gt; CSS block and a guidance entry, so half-built types cannot ship), and no two templates share an identical class-token sequence (a cheap copy-paste detector, not a true structural oracle: it catches a type that was forked and barely changed, but it will not flag two types that render the same shape under different class names).&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_card_registry.py — enforce the registry, flag copy-paste duplicates.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Python 3.9+ for str.removeprefix and the built-in dict[...] / tuple[...] generics.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; itertools &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; combinations&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;ASSETS &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;assets&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;CSS &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (ASSETS &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;diagram.css&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).read_text()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;GUIDE &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;SKILL.md&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).read_text()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# cosmetic-only classes: accent colours and the byline marker carry no structure&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;COSMETIC &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;green&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;grey&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;pink&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;brand&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; card_types&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; dict[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, Path]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # the registry IS the set of template files&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {p.stem.removeprefix(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-template-&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;): p&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ASSETS.glob(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-template-*.html&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class_signature&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(html: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; tuple[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, ...]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # the ordered sequence of class tokens, with nesting discarded. Strip text/ids,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # split multi-class strings, drop cosmetic accents, and normalize the canvas&apos;s own&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # `card &amp;#x3C;type&gt;` to just `card` so the type name doesn&apos;t make every type trivially&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # unique. This is a class-token fingerprint, NOT a parsed DOM tree: two templates&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # that nest the same classes differently will collide, and two that use different&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # class names for the same layout will not. It is a duplicate smell, not a proof.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    sig: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; attr &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.findall(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&apos;class=[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\&apos;]([^&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\&apos;]+)[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\&apos;]&apos;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, html):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        tokens &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [t &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; attr.split() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; COSMETIC]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;card&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tokens:        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the canvas element; keep only the shared marker&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            tokens &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        sig.extend(tokens)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; tuple&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(sig)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_every_type_is_fully_wired&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; name, path &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; card_types().items():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # match on real selector / filename boundaries so `flow` can&apos;t satisfy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # `flowchart` and a name buried in a CSS comment can&apos;t false-pass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.search(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;rf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;\.card\.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;re.escape(name)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;\b&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, CSS), \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;: no .card.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; CSS block&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.search(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;rf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-template-&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;re.escape(name)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;\.html&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, GUIDE), \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;: no SKILL.md guidance&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_no_two_templates_share_a_class_signature&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    sigs &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {n: class_signature(p.read_text()) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; n, p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; card_types().items()}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (a, sa), (b, sb) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; combinations(sigs.items(), &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sa &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sb, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; and &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; have an identical class sequence; did one fork the other?&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first test fails the moment a template ships without its CSS or its guidance, which keeps a new word from entering the vocabulary half-defined. The second fails the moment two types carry an identical class sequence, the cheap tell that one was forked from another and barely touched; a real merge still needs a human to confirm the two shapes are the same. So recurrence is what admits a new type, and the duplicate signal is what flags two of them for merging. Anything that is neither stays a small, separate file, and its blast radius is limited to itself.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The library keeps growing as new post shapes call for their own treatment. Each new card has to clear the same recurrence bar rather than a novelty one, and that is what has kept the family small enough to reason about so far.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nngroup.com/articles/comparison-tables/&quot;&gt;Nielsen Norman Group: Comparison Tables for Products, Services, and Features&lt;/a&gt; — when a comparison grid genuinely helps a decision.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction&quot;&gt;Sandi Metz: The Wrong Abstraction&lt;/a&gt; — why duplication beats a premature shared abstraction.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kentcdodds.com/blog/aha-programming&quot;&gt;Kent C. Dodds: AHA Programming&lt;/a&gt; — avoid hasty abstraction; let the shape reveal itself.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)&quot;&gt;Rule of three (computer programming)&lt;/a&gt; — let a pattern recur about three times before you extract it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ghostwriter 0.5.0: matrix card type (comparison grid) (&lt;a href=&quot;https://github.com/natejswenson/claude-skills/commit/03b5c38e34b6efdbe6630bbc9e5926d9f3240614&quot;&gt;03b5c38&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Handing an AI client a persona over MCP, and learning to resolve it late</title><link>https://natejswenson.com/devlog/local-fitness/v0.11.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/local-fitness/v0.11.0/</guid><description>The coach voice now follows me into Claude Code through the MCP server&apos;s instructions field. Getting it right meant resolving the persona at connect time instead of import time, and failing safe when the lookup misses. Here is the full build.</description><pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;v0.11.0 taught the fitness agent’s coach voice to follow me into Claude Code. I had wired the selectable coach persona into the two fitness slash commands and called it done, but the other way I use the data, asking Claude Code a question and letting it call the tools, had no voice at all. It answered in the default tone, because nothing told it I had picked a coach. Now the fitness MCP server hands the persona to the client as server-level instructions, resolved fresh on every connect. Pick hardass, and the tool-driven answers read hardass too.&lt;/p&gt;
&lt;p&gt;The feature is small. The part worth writing about is the mechanism: how an MCP server hands a client a persona at all, why that read has to happen at connect time rather than import time, and what to fall back to when the lookup misses. Here is the end-to-end build.&lt;/p&gt;
&lt;h2&gt;Setup: a coach profile to resolve&lt;/h2&gt;
&lt;p&gt;One prerequisite before any of this: &lt;code&gt;pip install mcp&lt;/code&gt; (SDK 1.x), and &lt;code&gt;pip install pytest&lt;/code&gt; for the tests at the end. SQLite ships with Python, so there is nothing else to add.&lt;/p&gt;
&lt;p&gt;The persona lives in SQLite next to the rest of the fitness data. One table holds the available coaches and a flag for the active one, and a thin accessor returns whichever profile is active. This is the value the server will eventually advertise to the client.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/db.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sqlite3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclasses &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;SCHEMA &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;CREATE TABLE IF NOT EXISTS coach_profiles (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    id             INTEGER PRIMARY KEY,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    name           TEXT NOT NULL,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    persona_prompt TEXT NOT NULL,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    is_active      INTEGER NOT NULL DEFAULT 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; CoachProfile&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    name: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    persona_prompt: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; connect&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; sqlite3.Connection:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # One place resolves the DB path, so the server and the tests open the same file.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sqlite3.connect(os.environ.get(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;FITNESS_DB&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness.db&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; init_schema&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(conn: sqlite3.Connection) -&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    conn.executescript(SCHEMA)          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# runs at app startup, not at import&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; get_active_coach_profile&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(conn: sqlite3.Connection) -&gt; CoachProfile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    row &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; conn.execute(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;SELECT name, persona_prompt FROM coach_profiles WHERE is_active = 1 LIMIT 1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    ).fetchone()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; CoachProfile(row[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], row[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; row &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The detail that matters for everything below is the ordering: &lt;code&gt;init_schema&lt;/code&gt; runs when the app starts, which is a moment after the Python modules finish importing. Any code that calls &lt;code&gt;get_active_coach_profile&lt;/code&gt; before startup is reading a table that does not exist yet.&lt;/p&gt;
&lt;h2&gt;Handing a persona over MCP&lt;/h2&gt;
&lt;p&gt;The Model Context Protocol is the open standard Anthropic introduced for connecting models to tools and data (&lt;a href=&quot;https://www.anthropic.com/news/model-context-protocol&quot;&gt;Introducing MCP&lt;/a&gt;). The hook I leaned on lives in the connection handshake. When a client initializes a session, the server’s response can include an optional &lt;code&gt;instructions&lt;/code&gt; field, described in the spec as guidance for the client to use during the session (&lt;a href=&quot;https://modelcontextprotocol.io/specification/2025-03-26/basic/lifecycle&quot;&gt;MCP: Lifecycle&lt;/a&gt;). That is the right shape for a persona. Instead of the voice living only inside my slash commands, the server advertises it at connect time and it rides along with the connection.&lt;/p&gt;
&lt;p&gt;In the Python SDK, &lt;code&gt;FastMCP&lt;/code&gt; takes that &lt;code&gt;instructions&lt;/code&gt; value and the low-level server returns it inside the initialize response via &lt;code&gt;create_initialization_options&lt;/code&gt; (&lt;a href=&quot;https://github.com/modelcontextprotocol/python-sdk&quot;&gt;MCP Python SDK&lt;/a&gt;). The naive version passes a string straight into the constructor.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/server.py  (the version that looks fine and is wrong)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.server.fastmcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;BUG&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;: this runs at import, before init_schema has created the table.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;profile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.get_active_coach_profile(db.connect())   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# reads a missing table -&gt; crash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;mcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, instructions&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;profile.persona_prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; profile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This crashes a fresh clone before anything runs, because the module imports before the schema exists. Not breaking the fresh-clone start is the one rule this repo has, so the read cannot happen here.&lt;/p&gt;
&lt;h2&gt;Resolve at connect, not at import&lt;/h2&gt;
&lt;p&gt;The fix is not to special-case startup. It is to move when the read happens, from module load to the moment a client connects. I wrap the lookup in a resolver that returns &lt;code&gt;None&lt;/code&gt; on any failure, then override &lt;code&gt;create_initialization_options&lt;/code&gt; so the resolver runs on every initialize rather than once.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/server.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; contextlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; closing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.server.fastmcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.server.lowlevel &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.server.models &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; InitializationOptions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; server_instructions&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Resolve the active persona lazily, on each connect, and fail safe to None.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    try&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; closing(db.connect()) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; conn:               &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# closing() so the read can&apos;t leak a connection&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            profile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.get_active_coach_profile(conn)    &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# schema exists by connect time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    except&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Exception&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None              &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# a missing voice is fine; a crashed connect is not&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; profile.persona_prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; profile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; FreshInstructionsServer&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(Server):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; create_initialization_options&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;args, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;**&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;kwargs) -&gt; InitializationOptions:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        opts &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; super&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;().create_initialization_options(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;args, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;**&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;kwargs)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        opts.instructions &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; server_instructions()    &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# recomputed for every initialize&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; opts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; build_server&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; FastMCP:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    mcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)              &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# no instructions read at construction&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Private-API seam (MCP SDK 1.x): FastMCP wraps a low-level Server on _mcp_server, and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # swapping its class is the smallest way to override create_initialization_options. This&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # reaches past the public surface; on the 2.0 line the class was renamed and the attribute&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # moved to _lowlevel_server, so confirm against your installed version and prefer a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # supported hook if the SDK exposes one.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    mcp._mcp_server.__class__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FreshInstructionsServer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Building &lt;code&gt;FastMCP&lt;/code&gt; with no instructions keeps import time cheap and side-effect free, which is the property that lets a fresh clone start. The override then runs &lt;code&gt;server_instructions&lt;/code&gt; during each connection’s initialize. For a stdio client that spawns a new process per connection the read is naturally fresh; for a long-running HTTP server the override is what keeps it fresh per session. Deferring the read past startup carried a bonus I did not plan: the value is now live. Change the active profile and the next connection picks it up with no restart, so the staleness limitation I was about to write down as an accepted tradeoff simply disappeared.&lt;/p&gt;
&lt;p&gt;The “schema exists by connect time” claim rests on the entry point, which creates the table at startup before serving anything. That ordering is the whole reason the deferred read is safe.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/transports.py   -&gt;  python -m fitness.transports&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.server &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    db.init_schema(db.connect())   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# startup: create the table before any connect resolves it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    build_server().run()           &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# serve MCP over stdio (FastMCP&apos;s default transport)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __name__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    main()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Failing safe when the lookup misses&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;except&lt;/code&gt; that returns &lt;code&gt;None&lt;/code&gt; is doing real work, not papering over a bug. It encodes the fail-safe defaults principle from Saltzer and Schroeder, generalized past access control: when something goes wrong, fall back to the safe state rather than the broken one (&lt;a href=&quot;https://en.wikipedia.org/wiki/The_Protection_of_Information_in_Computer_Systems&quot;&gt;The Protection of Information in Computer Systems&lt;/a&gt;). For a voice feature the safe state is easy to name. A missing personality is a perfectly fine degraded experience, while a connection that errors out is not. There is a second reason to design for the miss: the spec says &lt;code&gt;instructions&lt;/code&gt; is optional and whether the client applies it is the client’s call, so this is a best-effort channel rather than a contract. That is why the slash command stays the guaranteed path for the coach voice and the MCP route is the bonus that lights up when the client honors the server’s instructions. A &lt;code&gt;None&lt;/code&gt; return at either end, a failed read or a client that ignores the field, lands in the same harmless place: a plain voice.&lt;/p&gt;
&lt;h2&gt;Use it from a client&lt;/h2&gt;
&lt;p&gt;For a desktop or editor MCP client, point it at the stdio entry point and let the client launch the process (&lt;a href=&quot;https://modelcontextprotocol.io/docs/develop/connect-local-servers&quot;&gt;MCP: Connect to local servers&lt;/a&gt;). Nothing in the config mentions the persona, because the persona is resolved server-side at connect.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;mcpServers&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    &quot;fitness&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      &quot;command&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;python&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      &quot;args&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;-m&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness.transports&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      &quot;env&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;FITNESS_DB&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;/home/me/fitness.db&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On connect, the client’s initialize response now carries the active persona in its &lt;code&gt;instructions&lt;/code&gt; field. With the hardass profile active, the relevant slice of that response looks like this, and a client that honors it shifts the tone of every tool-driven answer to match.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;protocolVersion&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;2025-03-26&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;instructions&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;You are a hardass strength coach. Be blunt, no hedging, push for one hard thing today.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Switch the active row to a gentler coach, reconnect, and the next initialize carries the new text. No restart, no client-side change.&lt;/p&gt;
&lt;h2&gt;Verify it&lt;/h2&gt;
&lt;p&gt;The two decisions worth defending are testable: the persona resolves at connect rather than at import, and the resolver fails safe. Both became regression tests so the deferral and the fallback cannot quietly rot.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_persona_instructions.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sqlite3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; contextlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; closing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db, server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_build_does_not_read_db&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(monkeypatch):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Building the server must not touch the DB; a fresh clone has no schema yet.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; boom&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;a, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;**&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;k):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        raise&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; AssertionError&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;connect() called at construction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setattr(db, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;connect&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, boom)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    server.build_server()        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# must not raise&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_instructions_resolve_at_connect&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(tmp_path, monkeypatch):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    db_path &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tmp_path &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;fitness.db&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # The server resolves the persona through db.connect(), so the test&apos;s write and the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # server&apos;s read have to land in the same file; point connect() at it for both.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setattr(db, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;connect&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: sqlite3.connect(db_path))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; closing(db.connect()) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; conn:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        db.init_schema(conn)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        conn.execute(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;INSERT INTO coach_profiles (name, persona_prompt, is_active) VALUES (?, ?, 1)&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            (&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;hardass&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Be blunt.&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        conn.commit()            &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# without the commit the row never reaches the server&apos;s connection&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    mcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; server.build_server()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    opts &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp._mcp_server.create_initialization_options()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; opts.instructions &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;Be blunt.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_failed_lookup_fails_safe&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(monkeypatch):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setattr(db, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;connect&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: (_ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ()).throw(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;RuntimeError&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; server.server_instructions() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# safe state, not an exception&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first test fails the moment someone moves the profile read back into construction. The second proves the persona actually reaches the initialize response. The third fails if anyone removes the &lt;code&gt;except&lt;/code&gt; that turns a bad read into a plain voice.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The honest caveat is the one from the spec: whether Claude Code applies server instructions is the client’s decision, so the next step is confirming in a real session that the tool-driven tone actually shifts, with the slash command as the guaranteed path either way.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/news/model-context-protocol&quot;&gt;Anthropic: Introducing the Model Context Protocol&lt;/a&gt; — what MCP is and why it exists.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://modelcontextprotocol.io/specification/2025-03-26/basic/lifecycle&quot;&gt;MCP Specification: Lifecycle&lt;/a&gt; — the optional &lt;code&gt;instructions&lt;/code&gt; field a server returns at initialize, applied at the client’s discretion.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/modelcontextprotocol/python-sdk&quot;&gt;MCP Python SDK&lt;/a&gt; — FastMCP and the low-level server’s &lt;code&gt;create_initialization_options&lt;/code&gt;, where the &lt;code&gt;instructions&lt;/code&gt; value is produced.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://modelcontextprotocol.io/docs/develop/connect-local-servers&quot;&gt;MCP: Connect to local servers&lt;/a&gt; — pointing a desktop client at a local stdio server with command and args.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/The_Protection_of_Information_in_Computer_Systems&quot;&gt;Saltzer &amp;amp; Schroeder: The Protection of Information in Computer Systems&lt;/a&gt; — fail-safe defaults: fall back to the safe state.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat: coach profile carries into tool-driven Claude Code chat (MCP instructions, 0.11.0) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/780c9cba35537ba9b727f9e47517fa2c4a1ee4d2&quot;&gt;780c9cb&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;docs: revise MCP-persona design per quality-gate (2 rounds + look-harder, 4-&amp;gt;0) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/fbf3b4414e0499e017478c0d142f23c3806a116d&quot;&gt;fbf3b44&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;docs: design for coach persona as MCP server instructions (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/559c2e98daa4de6a8da1aaf3c48d9a594f168eeb&quot;&gt;559c2e9&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Selectable coach tones, and making the model&apos;s dials actually do something</title><link>https://natejswenson.com/devlog/local-fitness/v0.10.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/local-fitness/v0.10.0/</guid><description>The daily brief&apos;s voice is now a profile you choose. The real work wasn&apos;t the personas, it was making the tuning numbers deterministic instead of decorative, and testing all four tones against expectations rather than reading a sample.</description><pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;The daily brief used to have one voice: supportive when things were going well, a roast when I was slipping. That blend was mine and baked into the prompt. v0.10.0 makes it a profile you choose. There are four: supportive (your biggest believer), neutral (the emotion removed), hardass (never satisfied), and adaptive (the old behavior, kept as the default so a fresh clone is unchanged). Each is a real file you can read and edit, with a written voice and a set of numbers, and you switch with a config command.&lt;/p&gt;
&lt;p&gt;The personas were the easy half. The two things that actually took thought were making the numbers mean something and proving the tones behave. Both generalize past a fitness app, so here is the end-to-end build: profiles as files, a prompt builder that turns one of those numbers into a real code switch, the call site that uses it, and the test that keeps it honest.&lt;/p&gt;
&lt;h2&gt;A profile is a file, not a personality&lt;/h2&gt;
&lt;p&gt;The first decision is where a tone lives. Hard-coding four prompt strings in a &lt;code&gt;match&lt;/code&gt; statement works until you want to read them side by side or tweak one without redeploying. So each tone is a flat config file: a block of prose that sets the voice, and a few numbers that the code reads. Keeping this kind of static app data in declarative files instead of code is the old separate-data-from-code instinct; it rhymes with twelve-factor config, though strictly that factor targets settings that vary between deploys, and these presets are the same in every deploy (&lt;a href=&quot;https://12factor.net/config&quot;&gt;The Twelve-Factor App: Config&lt;/a&gt;). A reader can open &lt;code&gt;hardass.toml&lt;/code&gt; and see exactly what that coach is, prose and dials together.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# tones/hardass.toml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name = &quot;hardass&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;voice = &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;You are a demanding strength coach who is never quite satisfied.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Acknowledge effort in one line, then push for the next level.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Translate jargon, keep it short, never pad with praise.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;harsh = true          # read by code, not just the model&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;warmth = 2            # 1-10, lives in the prose only&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;target_pressure = 9   # 1-10, lives in the prose only&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The other three are the same shape. &lt;code&gt;adaptive&lt;/code&gt; is the default, so it has to exist for a fresh clone to boot, and &lt;code&gt;supportive&lt;/code&gt; is the soft counterweight the tests lean on, so both are real files too. &lt;code&gt;neutral&lt;/code&gt; is the same pattern with the warmth dialed flat:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# tones/adaptive.toml: the default, the old blended behavior&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name = &quot;adaptive&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;voice = &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;You read the week. Celebrate real wins warmly, but when the user is&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;slipping against a goal, name the gap and push for the next level.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Translate jargon, keep it short.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;harsh = true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;warmth = 6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;target_pressure = 6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# tones/supportive.toml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name = &quot;supportive&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;voice = &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;You are the user&apos;s biggest believer. Lead with what went well,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;frame every gap as the next small step, never scold.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Translate jargon, keep it short.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;harsh = false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;warmth = 9&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;target_pressure = 3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# tones/neutral.toml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name = &quot;neutral&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;voice = &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;You are a flat, factual coach. State what the data shows and the next&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;action, with no encouragement and no scolding.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Translate jargon, keep it short.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;harsh = false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;warmth = 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;target_pressure = 5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the split that the rest of the post turns on. &lt;code&gt;warmth&lt;/code&gt; and &lt;code&gt;target_pressure&lt;/code&gt; are numbers I hand to the model inside the prose, and the model interprets them as best it can. &lt;code&gt;harsh&lt;/code&gt; is a boolean my code reads directly. They look like peers in the file; they are not peers in how much I can trust them, and the next section is why.&lt;/p&gt;
&lt;h2&gt;Load the profile into something typed&lt;/h2&gt;
&lt;p&gt;Before any of that is useful, the files need to become a typed object the rest of the program can pass around. A small loader reads the TOML, validates that the fields exist, and returns a frozen dataclass. Doing the validation once at load time means a malformed tone fails loudly at startup rather than silently producing a weird brief at 6am. This needs Python 3.11+, where &lt;code&gt;tomllib&lt;/code&gt; is in the standard library (on older versions the &lt;code&gt;tomli&lt;/code&gt; backport is a drop-in, and the &lt;code&gt;int | None&lt;/code&gt; annotations below want 3.10+).&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tones/profile.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tomllib&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclasses &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;TONES_DIR &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(__file__).parent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@dataclass&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(frozen&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Profile&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    name: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    voice: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;          # the prose the model reads&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    harsh: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;         # the dial the code reads&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    warmth: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    target_pressure: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; load_profile&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(name: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; Profile:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    path &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; TONES_DIR &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;.toml&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; path.exists():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        raise&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; ValueError&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;unknown tone &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;!r&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;; have &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;available_tones()&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    data &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tomllib.loads(path.read_text())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Profile(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        name&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;data[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        voice&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;data[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;voice&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;].strip(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        harsh&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(data[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;harsh&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        warmth&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(data[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;warmth&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        target_pressure&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(data[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;target_pressure&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; available_tones&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; sorted&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(p.stem &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; TONES_DIR.glob(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;*.toml&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;load_profile(&amp;quot;hardass&amp;quot;)&lt;/code&gt; gives back a &lt;code&gt;Profile&lt;/code&gt; with a real &lt;code&gt;harsh&lt;/code&gt; flag, and an unknown tone name is an error with a helpful message instead of a &lt;code&gt;FileNotFoundError&lt;/code&gt; three layers down.&lt;/p&gt;
&lt;h2&gt;A number next to a prompt is a hope, not a control&lt;/h2&gt;
&lt;p&gt;The tempting design is to write a persona with prose and a dial, “harshness: 9,” and assume the model reads the dial and acts on it. It might. It might also glance at a 9 sitting next to a paragraph and do its own thing. A knob the system can’t falsify is just decoration you’ll end up trusting anyway.&lt;/p&gt;
&lt;p&gt;So for the parts of the brief with a concrete goal, like step count and plan adherence, the profile decides in code whether the harsh instruction block goes into the prompt at all. A harsh profile assembles that block, a soft profile leaves it out, and the prose voice handles the rest. The model never sees a number it has to honor for this behavior; it sees text that is either present or absent.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# brief/prompt.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclasses &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tones.profile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Profile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;HARSH_BLOCK &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;Hold the user to their goal. If steps or plan-adherence fall short of &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;target, say so directly and push for more. Do not soften a miss.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Goals&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    step_target: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    plan_id: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; has_concrete_targets&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&quot;&quot;True only when there is a measurable goal to be harsh about.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.step_target &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;or&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.plan_id)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; build_brief_prompt&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(base: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, profile: Profile, goals: Goals) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;base&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n\n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;profile.voice&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # the soft dials go into the prose for the model to interpret as best it can&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;Calibrate warmth to &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;profile.warmth&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/10 and &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;target pressure to &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;profile.target_pressure&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/10.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; profile.harsh &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; goals.has_concrete_targets():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; HARSH_BLOCK   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# a switch in code, not a hope about the model&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; prompt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is the distinction Anthropic draws between workflows and agents: a workflow orchestrates the model through predefined code paths when you want predictability, rather than letting the model decide (&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt;). The reliable lever isn’t a number I hope the model honors, it’s what I deterministically choose to put in front of it, which is the whole idea behind treating context as something you engineer rather than dump (&lt;a href=&quot;https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents&quot;&gt;Anthropic: Effective context engineering for AI agents&lt;/a&gt;). The dial now flips a real switch in code, and the harsh block only appears when there is a concrete target to be harsh about.&lt;/p&gt;
&lt;p&gt;The rule I took from it: if a configuration value can’t change something you can observe and assert, it isn’t configuration, it’s a comment.&lt;/p&gt;
&lt;h2&gt;Assemble the brief for a chosen profile&lt;/h2&gt;
&lt;p&gt;The call site is where the pieces meet. A config command writes the chosen tone name to settings, the brief loop reads it back, loads that profile, and builds the prompt around the day’s snapshot. The brief loop never branches on tone itself; it just loads whatever profile is configured and hands the assembled prompt to the model.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# brief/run.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; anthropic &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Anthropic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tones.profile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_profile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; brief.prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_brief_prompt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db, settings&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; _complete&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(prompt: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    client &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Anthropic()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    resp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; client.messages.create(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        model&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;claude-sonnet-4-5&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        max_tokens&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;600&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        messages&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: prompt}],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.content[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;].text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; run_daily_brief&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(day: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    profile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_profile(settings.get(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;tone&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, default&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;adaptive&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    snapshot &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.snapshot_for(day)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    goals &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.goals_for(day)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    base &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Write today&apos;s training brief from this snapshot:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;snapshot&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_brief_prompt(base, profile, goals)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _complete(prompt)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# switching tone is a one-line config change, no redeploy:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;#   $ fitness config set tone hardass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;db&lt;/code&gt;, &lt;code&gt;settings&lt;/code&gt;, and the &lt;code&gt;fitness config&lt;/code&gt; command are this app’s own data and settings layer; any key/value store you already have works in their place. Same loop, same snapshot, four possible voices, and exactly one of them carries the harsh block, decided before the request ever leaves the machine.&lt;/p&gt;
&lt;h2&gt;Test the tone, don’t eyeball it&lt;/h2&gt;
&lt;p&gt;The other half was testing the way I keep saying I want to, against expectations instead of by reading one nice sample. The deterministic part is easy and exact: assert that the harsh block is present for hardass with a real target, and absent for supportive. That test fails the moment someone renames the flag, inverts the condition, or drops the target check. The fakes those tests use are one-liners that build a &lt;code&gt;Goals&lt;/code&gt; with and without a &lt;code&gt;step_target&lt;/code&gt;, so they satisfy the same &lt;code&gt;has_concrete_targets()&lt;/code&gt; contract the real object does:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/fakes.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; brief.prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Goals&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; goals_with_target&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; Goals:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Goals(step_target&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;8000&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; goals_without_target&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; Goals:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Goals()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_brief_prompt.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tones.profile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_profile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; brief.prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_brief_prompt, HARSH_BLOCK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tests.fakes &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; goals_with_target, goals_without_target&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;BASE &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;Write today&apos;s training brief.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_harsh_block_present_for_hardass&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_brief_prompt(BASE, load_profile(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;hardass&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), goals_with_target())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; HARSH_BLOCK &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; prompt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_harsh_block_absent_for_supportive&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_brief_prompt(BASE, load_profile(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;supportive&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), goals_with_target())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; HARSH_BLOCK &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; prompt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_harsh_block_skipped_without_a_concrete_target&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # even a harsh profile has nothing to be harsh about with no goal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_brief_prompt(BASE, load_profile(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;hardass&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), goals_without_target())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; HARSH_BLOCK &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; prompt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model’s actual words are the part you can’t assert exactly, and that is where people give up and go back to eyeballing. The move is to assert the properties instead. Anthropic’s own guidance is to define evals around the things that matter rather than expecting deterministic output (&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt;). So a scorer runs every available profile against a fixed snapshot and checks the properties each output must hold, then A/B-checks the tones against expectations rather than against my gut.&lt;/p&gt;
&lt;p&gt;The wrapper that makes that possible lives in a test-only module and imports the production pieces, never the other way around. That direction matters: &lt;code&gt;brief/run.py&lt;/code&gt; has no idea the harness exists, so importing it at 6am to write the real brief never drags in &lt;code&gt;pytest&lt;/code&gt; or the &lt;code&gt;tests&lt;/code&gt; package. &lt;code&gt;generate_for_test&lt;/code&gt; builds the same prompt against one fixed snapshot and returns a small parsed object, and &lt;code&gt;tone_category()&lt;/code&gt; is a second model call grading the first, an LLM judge reading the output and naming the coach who must have written it.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/harness.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclasses &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tones.profile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_profile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; brief.prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_brief_prompt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; brief.run &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _complete&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tests.fakes &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; goals_with_target&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;FIXED_SNAPSHOT &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;steps 4200/8000, zone-2 12 min, HRV 41ms, push-day skipped&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; BriefOutput&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    tone: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    text: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; matches_schema&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# same shape regardless of voice&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.text.strip()) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; translated_jargon&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:     &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# best-effort signal, not a hard gate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; all&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(term &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; term &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;HRV&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;zone-2&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; directive_count&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# proxy for how hard it pushes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        starts &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Do &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Push&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Get &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Hit &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; sum&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(line.strip().startswith(starts) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; line &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.text.splitlines())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; tone_category&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the LLM judge, below&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; judge_tone(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.text)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; generate_for_test&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(tone: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; BriefOutput:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    profile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_profile(tone)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    base &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Write today&apos;s training brief from this snapshot:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;FIXED_SNAPSHOT&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_brief_prompt(base, profile, goals_with_target())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; BriefOutput(tone&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;tone, text&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;_complete(prompt))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; judge_tone&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(text: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # one judge call: hand the model the brief, get back a single category word&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _complete(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;Reply with exactly one word from &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;[supportive, neutral, hardass, adaptive] naming this brief&apos;s tone:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; verdict.strip().lower()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every method on &lt;code&gt;BriefOutput&lt;/code&gt; except &lt;code&gt;tone_category()&lt;/code&gt; is a cheap local check; &lt;code&gt;tone_category()&lt;/code&gt; and &lt;code&gt;generate_for_test()&lt;/code&gt; are the parts that actually call the model, which is why the property tests below are gated. They hit the live API: a real key, real cost, and output that drifts run to run. Gating on the key’s presence would backfire, since anyone working on this app already has &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; exported for &lt;code&gt;run.py&lt;/code&gt;, so a plain &lt;code&gt;pytest&lt;/code&gt; would charge them on every commit. The gate is an explicit opt-in instead: they run only when you set &lt;code&gt;RUN_LLM_TESTS=1&lt;/code&gt;, so a plain &lt;code&gt;pytest&lt;/code&gt; run and CI skip them, and they run on their own cadence (or against a mock) rather than in the commit loop.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_tone_properties.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pytest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tones.profile &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; available_tones&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tests.harness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; generate_for_test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;requires_llm &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pytest.mark.skipif(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os.environ.get(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;RUN_LLM_TESTS&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    reason&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;set RUN_LLM_TESTS=1 to run the paid LLM property tests&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@requires_llm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@pytest.mark.parametrize&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;tone&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, available_tones())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_every_tone_returns_a_well_formed_brief&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(tone):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # the structural contract holds for every voice, exactly&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    out &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; generate_for_test(tone)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; out.matches_schema()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@requires_llm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_hardass_pushes_harder_than_supportive&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # discriminating property: assert a contrast, not an exact label.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # don&apos;t ask one judge call to nail four overlapping categories; the two&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # tones meant to be furthest apart should diverge on a measurable axis.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    hard &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; generate_for_test(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;hardass&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    soft &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; generate_for_test(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;supportive&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; hard.directive_count() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; soft.directive_count()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; hard.tone_category() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; soft.tone_category()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The reason &lt;code&gt;tone_category()&lt;/code&gt; is asserted only as a contrast between an opposed pair, never as exact equality per tone, is that adaptive and hardass assemble nearly identical prompts and neutral and supportive overlap, so demanding the judge return the exact word for all four would flake without proving anything. The directive count is the real discriminator; the judge call just confirms the two ends read as different coaches. The jargon check rides along as a logged signal rather than a gate, since “no raw HRV or zone term ever survives” is a hope about the model, not a contract it owes me. The non-deterministic part gets contrast properties, the deterministic part gets exact ones, and the tone stops being a vibe I personally sign off on. The split also decides when each runs: the deterministic prompt tests are free and fast, so they run on every commit; the property tests cost a model call apiece and drift run to run, so they sit behind the &lt;code&gt;RUN_LLM_TESTS&lt;/code&gt; opt-in and run on their own cadence rather than in the commit loop.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The profiles are fixed presets today. The natural next step is letting the saved coaching notes nudge a chosen profile on a specific point, since the precedence for that is already written into the prompt.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt; — workflows orchestrate models through predefined code paths for predictability.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents&quot;&gt;Anthropic: Effective context engineering for AI agents&lt;/a&gt; — control what goes into the model’s context deliberately.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt; — evaluate non-deterministic systems on the properties that matter.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://12factor.net/config&quot;&gt;The Twelve-Factor App: Config&lt;/a&gt; — keep configuration that varies between deployments in declarative data outside the code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat: selectable coach tone profiles for the daily brief (0.10.0) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/e53b005fb116adec77749197eba764565e4ca896&quot;&gt;e53b005&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;docs: per-profile A/B + quality scorer vs expected outcomes (mandatory, not optional) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/fc17711bc0a24230252a505d96b0bae5bfcd6464&quot;&gt;fc17711&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;docs: design for selectable coach tone profiles (supportive/neutral/hardass) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/f15df805e9578bcb5f44cf1ec80e5c1ee19336c8&quot;&gt;f15df80&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>A settings resolver that defaults to the present and fails safe on bad input</title><link>https://natejswenson.com/devlog/local-fitness/v0.9.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/local-fitness/v0.9.0/</guid><description>Five coaching decisions were hardcoded as my preferences. Turning them into user settings came down to one resolution order over three existing config homes, defaults that exactly reproduce the old behavior, and validation that falls back instead of silently grading you wrong.</description><pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;This app is public, but a handful of coaching calls were baked into the code as my preferences: whether a recovery walk counts toward an easy day, how close to the target distance counts as done versus partial, and how far back to look for a best effort when projecting a race finish. Reasonable defaults, but mine, and anyone cloning the repo was stuck with them. v0.9.0 pulls five of those out of the code and makes them settings, each defaulting to exactly the old hardcoded value, so a fresh clone behaves the same and nobody has to configure anything to start.&lt;/p&gt;
&lt;p&gt;The feature is small. The part worth writing about is the resolver underneath it: where a value comes from, what it defaults to, and what happens when it is wrong. Here is the full build, from the setting definitions through the call site and the test that keeps the defaults honest.&lt;/p&gt;
&lt;h2&gt;Setup: define the knobs in one typed registry&lt;/h2&gt;
&lt;p&gt;The wrong move would have been to scatter five new &lt;code&gt;os.environ.get&lt;/code&gt; calls through the grading code, each with its own inline default and its own ad hoc parsing. Instead every knob is declared once, in a single registry, with its type-bearing default and an optional validator. The default value doubles as the type the resolver coerces to, so there is no separate schema to keep in sync.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/config_defs.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclasses &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclass, field&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; typing &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Any, Callable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@dataclass&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(frozen&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Setting&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    key: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    default: Any                                   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# also defines the target type&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    validate: Callable[[Any], &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; field(default&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; v: True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; is_fraction&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(v: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0.0&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; v &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; is_positive&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(v: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; v &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Each default is exactly the value that used to be hardcoded.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;SETTINGS &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;count_recovery_walks&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: Setting(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;count_recovery_walks&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, False),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;done_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:        Setting(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;done_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0.95&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, is_fraction),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;partial_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:     Setting(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;partial_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0.70&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, is_fraction),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;best_effort_lookback&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: Setting(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;best_effort_lookback&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;90&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, is_positive),   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# days&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;projection_window&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:    Setting(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;projection_window&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;42&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, is_positive),      &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# days&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This keeps the configuration in one place with a defined shape rather than spread through the code, which is the twelve-factor instinct of treating config as a first-class, separated concern (&lt;a href=&quot;https://12factor.net/config&quot;&gt;The Twelve-Factor App: Config&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Build: resolve in one clear order&lt;/h2&gt;
&lt;p&gt;The app already had three places configuration could live: environment variables for deployment and secrets, a small settings table for per-user values like the step goal, and a notes file for the coach’s conversational memory. Adding a fourth mechanism for these knobs would have been the easy mistake. The discipline is fewer config sources with a defined precedence, not more sources each read on its own.&lt;/p&gt;
&lt;p&gt;So every knob resolves in one order: a value in the settings table wins, then an environment variable, then the built-in default. The resolver coerces the raw value to the type of the default and runs the validator, and anything missing, empty, malformed, or invalid falls back to the default rather than raising.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/config.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.config_defs &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Setting&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; resolve&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(s: Setting, db_settings: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;object&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;settings table  &gt;  environment variable  &gt;  built-in default.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    raw &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db_settings.get(s.key)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; raw &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (None, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        raw &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os.environ.get(s.key.upper())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; raw &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (None, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;):                &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# missing or empty -&gt; the safe default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; s.default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    try&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        value &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _coerce(raw, s.default)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    except&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;KeyError&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;ValueError&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;TypeError&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; s.default                 &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# unparseable -&gt; fall back, never error&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; value &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; s.validate(value) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; s.default  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# out of range -&gt; fall back&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; _coerce&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(raw, default):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; isinstance&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(default, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;):        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# bool first: bool(raw) makes any non-empty string truthy, e.g. bool(&quot;false&quot;) == True&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: True, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: False}[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(raw).strip().lower()]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(default)(raw)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two details here each prevent a different silent failure. The boolean branch is the one the review caught. The generic line is &lt;code&gt;type(default)(raw)&lt;/code&gt;, and for a bool default &lt;code&gt;type(default)&lt;/code&gt; is exactly &lt;code&gt;bool&lt;/code&gt;, so without the special case that line would call &lt;code&gt;bool(raw)&lt;/code&gt;, which is a trap: &lt;code&gt;bool(&amp;quot;false&amp;quot;)&lt;/code&gt; is &lt;code&gt;True&lt;/code&gt;, because every non-empty string is truthy, and &lt;code&gt;bool(&amp;quot;&amp;quot;)&lt;/code&gt; is &lt;code&gt;False&lt;/code&gt;. So a setting that defaults to on would flip off on an empty value, and the literal string &lt;code&gt;&amp;quot;false&amp;quot;&lt;/code&gt; would read as on. The dict lookup avoids both: it maps &lt;code&gt;&amp;quot;true&amp;quot;&lt;/code&gt; and &lt;code&gt;&amp;quot;false&amp;quot;&lt;/code&gt; explicitly and raises &lt;code&gt;KeyError&lt;/code&gt; on anything else (a typo, a number, &lt;code&gt;&amp;quot;yes&amp;quot;&lt;/code&gt;), which the caller turns into the safe default. One classic gotcha does not bite here: the bool case is checked first with &lt;code&gt;isinstance(default, bool)&lt;/code&gt; and returns before the generic &lt;code&gt;type(default)(raw)&lt;/code&gt; line is ever reached, and there is no &lt;code&gt;isinstance(default, int)&lt;/code&gt; branch for a bool to fall into, so the classic bool-is-a-subclass-of-int trap never applies. The trap only bites code that dispatches with &lt;code&gt;isinstance(default, int)&lt;/code&gt; before handling bool.&lt;/p&gt;
&lt;p&gt;The empty-string guards in &lt;code&gt;resolve&lt;/code&gt; do a separate job. They are not what stops the &lt;code&gt;bool(&amp;quot;&amp;quot;)&lt;/code&gt; bug; the dict branch already does that. Their job is resolution order: an empty value in the settings table (a cleared row, not an absent one) should fall through to the environment variable, and if that is empty too, on to the built-in default, rather than being coerced as if the user had typed something.&lt;/p&gt;
&lt;h2&gt;Build: fail safe, because the dangerous inputs are the quiet ones&lt;/h2&gt;
&lt;p&gt;Per-knob validation is half the job. The other half is the relationships between knobs. &lt;code&gt;done_fraction&lt;/code&gt; must be at least &lt;code&gt;partial_fraction&lt;/code&gt;, or the grade bands invert and a run that should read as “done” reads as “partial.” That cross-field rule cannot live in a single &lt;code&gt;Setting&lt;/code&gt;, so the loader checks it after resolving everything and falls back to the paired defaults if the pair is incoherent.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/config.py  (continued)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.config_defs &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SETTINGS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; load_settings&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(db_settings: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    cfg &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {key: resolve(s, db_settings) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; key, s &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SETTINGS.items()}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Cross-field invariant: the &quot;done&quot; band must sit above the &quot;partial&quot; band.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cfg[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;done_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cfg[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;partial_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        cfg[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;done_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SETTINGS[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;done_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;].default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        cfg[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;partial_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SETTINGS[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;partial_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;].default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cfg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every one of these failure modes shares a shape: none of them error, they just quietly change behavior. An empty value flipping a boolean. An inverted fraction pair reordering the grade bands. A negative lookback window erasing the projected finish. That is why each falls back to the default instead of taking effect, which is the fail-safe-defaults principle generalized from access control: on bad input, fall back to the safe state rather than the broken one (&lt;a href=&quot;https://en.wikipedia.org/wiki/The_Protection_of_Information_in_Computer_Systems&quot;&gt;Saltzer &amp;amp; Schroeder: fail-safe defaults&lt;/a&gt;). A loud failure would have been fine, I would have seen it. The real risk was a malformed knob that does not crash and just grades you wrong forever.&lt;/p&gt;
&lt;h2&gt;Use it: set one knob, read them all&lt;/h2&gt;
&lt;p&gt;Defaulting to the present means the common path needs no setup at all. A fresh clone reproduces the old behavior with zero configuration, which is convention over configuration in practice (&lt;a href=&quot;https://en.wikipedia.org/wiki/Convention_over_configuration&quot;&gt;Convention over configuration&lt;/a&gt;). When you do want to change something, you set it once and read the whole resolved config at the call site.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Per-user override: write to the settings table, takes effect immediately.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;fitness&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; set&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; count_recovery_walks&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Or set it for a deployment via the environment instead.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; COUNT_RECOVERY_WALKS&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/grade.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.config &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_settings&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; grade_run&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(run) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    cfg &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_settings(db.settings_dict())     &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# table &gt; env &gt; default, all resolved&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    ratio &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; run.distance_meters &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; run.target_meters&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ratio &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cfg[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;done_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;done&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ratio &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cfg[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;partial_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;partial&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;short&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# A 9.6 km run against a 10 km target, with stock defaults (done=0.95, partial=0.70):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;#   ratio = 0.96  -&gt;  grade_run(run) == &quot;done&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Set partial_fraction to an empty string and it does not break; it falls back to 0.70.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;db.settings_dict()&lt;/code&gt; is the thin read side of the settings table: it returns a plain &lt;code&gt;{key: str_value}&lt;/code&gt; map of whatever the user has set, all values as strings, which is exactly the raw shape &lt;code&gt;resolve&lt;/code&gt; expects. The &lt;code&gt;fitness config set&lt;/code&gt; command above is its write side. Both are project-specific adapters to my store, not part of the pattern: &lt;code&gt;settings_dict()&lt;/code&gt; just returns a &lt;code&gt;{str: str}&lt;/code&gt; map and the CLI just writes that store, so swap in your own (a config file, a different table, whatever you already have). No SQL or env parsing leaks into the grading logic. It asks &lt;code&gt;load_settings&lt;/code&gt; for a resolved dictionary and trusts that every value is either a valid override or a safe default.&lt;/p&gt;
&lt;h2&gt;Verify it: pin the defaults and the fallbacks&lt;/h2&gt;
&lt;p&gt;The one property worth pinning is that the defaults reproduce the old behavior exactly. That is a golden, or regression, test: a hand-authored snapshot of the known-good values asserted against the resolver’s output, so a later change cannot alter them unnoticed (&lt;a href=&quot;https://en.wikipedia.org/wiki/Regression_testing&quot;&gt;Regression testing&lt;/a&gt;). Its only job is to fail the day someone edits a default by accident. The second test covers the resolution order and the quiet failure modes: an empty settings-table value must fall through to the environment rather than error, and an inverted fraction pair must resolve back to the defaults.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_config.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.config &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_settings&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.config_defs &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SETTINGS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;OLD_HARDCODED &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {                       &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# what the values were before v0.9.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;count_recovery_walks&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: False,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;done_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0.95&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;partial_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0.70&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;best_effort_lookback&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;90&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;projection_window&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;42&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_defaults_match_old_behavior&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(monkeypatch):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; key &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SETTINGS:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        monkeypatch.delenv(key.upper(), raising&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;False)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# no env overrides in scope&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    cfg &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_settings(db_settings&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{})                 &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# and no table overrides&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cfg &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; OLD_HARDCODED                          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fails if any default drifts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_bad_input_falls_back_not_errors&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(monkeypatch):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; key &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SETTINGS:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        monkeypatch.delenv(key.upper(), raising&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;False)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# clear any stray env vars first&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setenv(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;COUNT_RECOVERY_WALKS&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# an env override is present&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    cfg &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_settings(db_settings&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;count_recovery_walks&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,                 &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# empty table value -&gt; fall through to env&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;done_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;0.40&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,                    &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# below partial -&gt; pair resets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;partial_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;0.70&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cfg[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;count_recovery_walks&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; True      &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# empty table value fell through to env&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cfg[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;done_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SETTINGS[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;done_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;].default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cfg[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;partial_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SETTINGS[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;partial_fraction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;].default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first test fails the moment a default changes meaning. The second fails if the empty-string fall-through guard is removed, because the empty table value would resolve to the default instead of the environment override, or if the cross-field check is removed, because the inverted pair would survive. Together they turn the two decisions that are easy to get quietly wrong into things the suite notices loudly.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The same pattern fits the knobs I left hardcoded for now, the baseline window and the fatigue and fitness time constants. Those are the next candidates once there is a real reason to tune them.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://12factor.net/config&quot;&gt;The Twelve-Factor App: Config&lt;/a&gt; — keep configuration in the environment, separate from code, with a defined shape.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Convention_over_configuration&quot;&gt;Convention over configuration&lt;/a&gt; — sensible defaults so the common case needs no setup.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/The_Protection_of_Information_in_Computer_Systems&quot;&gt;Saltzer &amp;amp; Schroeder: The Protection of Information in Computer Systems&lt;/a&gt; — fail-safe defaults, originally default-deny in access control; generalized here to: on bad input, fall back to the safe state.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Regression_testing&quot;&gt;Regression testing&lt;/a&gt; — a golden test that pins known-good values so later changes cannot alter them unnoticed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat: make grading + projection behavior user-configurable (0.9.0) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/8047c4e237028edc6ad6e0dd352cae3d64130105&quot;&gt;8047c4e&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;docs: design for user-configurable fitness behavior (grading knobs) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/f28e479101ef10122b814c6f2115a11e123cd09d&quot;&gt;f28e479&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Grade the state, not the calendar: a date is a stand-in for the question you actually care about</title><link>https://natejswenson.com/devlog/local-fitness/v0.8.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/local-fitness/v0.8.0/</guid><description>Two grading bugs traced to the same mistake: the grader read the calendar instead of the outcome, so today&apos;s finished run showed pending and a recovery walk read as a miss. The fix is a small decision table that grades by what actually happened, plus one verdict the UI reads instead of recomputing.</description><pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;I asked the coach how my training plan was going and two things were wrong. Today’s run, already synced from my watch, showed as “pending.” And Saturday, when I took a long recovery walk instead of a run, showed as a missed day. v0.8.0 fixes both, makes walks count on easy days, and corrects the plan tab so its row colors match the grade. The walk-counting change is a rules tweak. The part worth writing about is the shape underneath both bugs: the grader was reading the calendar as a proxy for “did this day succeed,” and the calendar drifts from that question right at the boundary called “today.”&lt;/p&gt;
&lt;p&gt;The fix is to grade the day by its outcome, not its date. A completed workout grades immediately, a rest day reads compliant, a half-finished run stays pending instead of booking half credit, and an un-synced day holds pending instead of booking a false miss. Here is the end-to-end build, from the state model to the tests that pin the boundaries down.&lt;/p&gt;
&lt;h2&gt;A date is a stand-in: model the state, not the clock&lt;/h2&gt;
&lt;p&gt;The “pending” bug came from grading against the clock. The grader held any day at or after the most recent sync as pending, so it would not call a day missed before the data had arrived. Reasonable, except the most recent synced day is today, so today was always pending even with a finished run sitting in the database. That is the classic split between when something happened and when the system got around to looking: stream systems name it event time versus processing time, where processing time tracks the machine’s wall clock and drifts from the data’s own timeline, especially near the present edge (&lt;a href=&quot;https://nightlies.apache.org/flink/flink-docs-stable/docs/concepts/time/&quot;&gt;Apache Flink: Timely Stream Processing&lt;/a&gt;). Grading on the wall clock means the answer changes with when you ask, not with what you did.&lt;/p&gt;
&lt;p&gt;So model the thing you actually care about. A day has a plan (what was scheduled) and an actual (what happened), and the grade is a function of those two, not of the date. This is a pure mapping: a decision table that takes (plan, actual) to a verdict, with no transitions and no memory carried between days (&lt;a href=&quot;https://en.wikipedia.org/wiki/Decision_table&quot;&gt;Decision table&lt;/a&gt;). Start with the model.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# grading/model.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclasses &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; datetime &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; enum &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Enum&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Plan&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(Enum):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    RUN  &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;run&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;     # a specific workout was scheduled&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    EASY &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;easy&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # an easy day; a recovery walk counts as done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    REST &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;rest&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # nothing scheduled, on purpose&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Actual&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(Enum):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    COMPLETED &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;completed&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # the planned work finished&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    WALK      &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;walk&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;       # a walk was recorded (counts as done on an easy day)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    PARTIAL   &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;partial&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # started, not finished (a run in progress)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    NONE      &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;none&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;       # nothing recorded for the day&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@dataclass&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(frozen&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Day&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    on: date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    plan: Plan&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    actual: Actual&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    is_synced: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;          # has this day&apos;s data arrived from the watch?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing here references “today.” A day carries everything the grader needs: what was asked, what was done, and whether the data is in. The calendar becomes one ordinary field instead of the hidden axis the whole decision pivots on.&lt;/p&gt;
&lt;h2&gt;grade_day: map state to verdict by outcome&lt;/h2&gt;
&lt;p&gt;Now the function. Grade the outcome first, then apply one narrow rule for uncertainty: hold pending only when the read is not already a settled success and the day genuinely is not finished, either because the data has not synced or because a run is still in progress. That keeps a finished day from ever sitting at pending, and keeps an un-synced or half-done day from booking a false miss or false half-credit.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# grading/grade.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; enum &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Enum&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grading.model &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Day, Plan, Actual&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Verdict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(Enum):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    DONE      &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;done&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;       # planned work finished&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    COMPLIANT &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;compliant&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # did what the day asked (a rest day taken at rest)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    MISSED    &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;missed&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;     # work was scheduled and none came&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    PARTIAL   &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;partial&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # internal to evaluate(); grade_day collapses it to PENDING, so the UI never sees it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    PENDING   &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;pending&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # can&apos;t judge yet; data may still arrive&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;SETTLED_SUCCESS &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {Verdict.DONE, Verdict.COMPLIANT}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; evaluate&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(day: Day) -&gt; Verdict:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Map (plan, actual) -&gt; verdict purely by outcome. No reference to the date.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; day.plan &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Plan.REST:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.COMPLIANT          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# rest grades compliant regardless of what&apos;s recorded&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; day.plan &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Plan.EASY &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; day.actual &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (Actual.COMPLETED, Actual.WALK):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.DONE               &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the headline rule: a walk counts as done on an easy day&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; day.actual &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Actual.COMPLETED:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.DONE               &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# planned work finished&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; day.actual &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Actual.PARTIAL:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.PARTIAL            &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# half a run, not yet anything&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.MISSED                 &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# scheduled work, nothing recorded (a walk on a run day lands here)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; grade_day&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(day: Day) -&gt; Verdict:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; evaluate(day)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SETTLED_SUCCESS:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; verdict                    &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# done / compliant grade immediately, today included&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # verdict is partial or missed: only final once the day is settled.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    settled &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; day.is_synced &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; day.actual &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Actual.PARTIAL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; settled &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.PENDING&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Trace the four cases that broke before. A completed run returns &lt;code&gt;DONE&lt;/code&gt; straight from &lt;code&gt;evaluate&lt;/code&gt;, regardless of date, so today no longer hangs. A rest day returns &lt;code&gt;COMPLIANT&lt;/code&gt; regardless of what was recorded, so a planned day off stops reading as a miss even if you logged an easy jog. A partial run is never “settled,” so it returns &lt;code&gt;PENDING&lt;/code&gt; rather than half credit that quietly heals later; a review round flagged that one before it shipped. &lt;code&gt;PARTIAL&lt;/code&gt; is therefore an internal value that &lt;code&gt;evaluate&lt;/code&gt; emits and &lt;code&gt;grade_day&lt;/code&gt; always folds into &lt;code&gt;PENDING&lt;/code&gt;, which means the four verdicts a caller can actually receive are &lt;code&gt;DONE&lt;/code&gt;, &lt;code&gt;COMPLIANT&lt;/code&gt;, &lt;code&gt;MISSED&lt;/code&gt;, and &lt;code&gt;PENDING&lt;/code&gt;. An un-synced day with nothing recorded is also not settled, so it holds &lt;code&gt;PENDING&lt;/code&gt; instead of a false miss. Only a synced day that scheduled work and recorded none returns &lt;code&gt;MISSED&lt;/code&gt;, which is the one case that is truly a miss.&lt;/p&gt;
&lt;p&gt;The headline walk-counting rule is the one explicit branch on &lt;code&gt;Plan.EASY&lt;/code&gt;: on an easy day a recorded walk maps to &lt;code&gt;DONE&lt;/code&gt; exactly like a completed workout, which is why Saturday’s recovery walk now grades green. On a &lt;code&gt;RUN&lt;/code&gt; day that same walk is not the scheduled run, so it falls through to &lt;code&gt;MISSED&lt;/code&gt;; the plan type, not the activity alone, decides whether a walk counts.&lt;/p&gt;
&lt;h2&gt;Grade a stretch of days&lt;/h2&gt;
&lt;p&gt;The UI grades a window of days and renders one verdict each. Because &lt;code&gt;grade_day&lt;/code&gt; is pure over the &lt;code&gt;Day&lt;/code&gt; model, scoring a week is a map, and the per-day verdicts fall straight out.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; datetime &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grading.model &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Day, Plan, Actual&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grading.grade &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;week &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    Day(date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2026&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), Plan.RUN,  Actual.COMPLETED, is_synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True),   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Sat target run&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    Day(date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2026&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;21&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), Plan.REST, Actual.NONE,      is_synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True),   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# planned rest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    Day(date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2026&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;22&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), Plan.EASY, Actual.WALK,      is_synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True),   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# easy day, a walk counts as done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    Day(date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2026&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;23&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), Plan.RUN,  Actual.PARTIAL,   is_synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True),   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# run in progress&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    Day(date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2026&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;24&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), Plan.RUN,  Actual.NONE,      is_synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;False),  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# today, not synced yet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    Day(date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2026&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;25&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), Plan.RUN,  Actual.NONE,      is_synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True),   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# scheduled, nothing ran&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; d &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; week:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(d.on, grade_day(d).value)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2026-06-20 done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2026-06-21 compliant&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2026-06-22 done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2026-06-23 pending&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2026-06-24 pending&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2026-06-25 missed&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adherence then reads off those verdicts, and the key choice is what to leave out: pending days are unsettled, so counting them either way is a guess. They drop out of the denominator entirely instead of dragging the rate down.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grading.grade &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day, Verdict&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; adherence&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(days) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    verdicts &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [grade_day(d) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; d &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; days]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    graded &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [v &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; v &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; verdicts &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; v &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.PENDING]  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# pending isn&apos;t a judgment yet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; graded:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    good &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; sum&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(v &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {Verdict.DONE, Verdict.COMPLIANT} &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; v &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; graded)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; good &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(graded)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;float | None&lt;/code&gt; return uses PEP 604 union syntax, which needs Python 3.10 or newer; on older runtimes write &lt;code&gt;Optional[float]&lt;/code&gt; instead. A pending day no longer counts against you, and it no longer needs a backfill job to “correct” the rate once the data lands; the next grade is simply right.&lt;/p&gt;
&lt;h2&gt;One verdict, computed once&lt;/h2&gt;
&lt;p&gt;The most useful catch came from review, not tests. The backend was clean and unit-tested, but a second-look review flagged that the plan tab colored each row by recomputing a pace miss on its own, independent of the verdict the grader had already produced. With walks now counting as done on easy days, a completed walk has a pace far slower than a run target, so that done day would have rendered red anyway. Two parts of the system computed “did this day succeed,” and they disagreed.&lt;/p&gt;
&lt;p&gt;That is the single-source-of-truth problem in miniature: every fact should have one authoritative home, and everything else should read from it rather than keep its own copy (&lt;a href=&quot;https://en.wikipedia.org/wiki/Single_source_of_truth&quot;&gt;Single source of truth&lt;/a&gt;). It is also the core of how React tells you to structure state, where you derive values from the source instead of storing or recomputing them separately so they cannot fall out of sync (&lt;a href=&quot;https://react.dev/learn/choosing-the-state-structure&quot;&gt;React: Choosing the State Structure&lt;/a&gt;), and it is Don’t Repeat Yourself applied to a decision rather than to text (&lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot;&gt;Don’t repeat yourself&lt;/a&gt;). So the row reads the verdict and nothing else.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// PlanRow.tsx — color comes from the verdict, never recomputed from pace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// Mirrors the four verdicts grade_day(...).value can actually return.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// (The server enum also has &quot;partial&quot;, but grade_day collapses it to &quot;pending&quot;, so it never reaches the client.)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Verdict&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;done&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;compliant&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;pending&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;missed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; GradedDay&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  verdict&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Verdict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// the one authoritative grade from the server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // ...plus whatever the row renders: date, pace, distance, etc.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; COLOR&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Record&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;Verdict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;&gt; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  done: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;row--green&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  compliant: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;row--green&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  pending: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;row--gray&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  missed: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;row--red&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; PlanRow&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;({ day }&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { day&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; GradedDay&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // day.verdict was produced once by grade_day on the server (its enum .value).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // This row does not look at pace or distance to second-guess it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;tr&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; className={&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;COLOR[day.verdict]&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&gt;{&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* cells */&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;tr&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the change I confirmed it with a screenshot: Saturday’s walk shows green at a 17 minute mile, today grades done, and adherence reads 100 percent. The unit tests had passed the whole time, because the grader was right; what they could not see was a second component re-deriving the grader’s decision and getting a different answer. That inconsistency lives between layers, not inside one, which is exactly the gap a fresh-eyes review is for.&lt;/p&gt;
&lt;h2&gt;Verify the boundaries&lt;/h2&gt;
&lt;p&gt;The decisions worth defending are the boundary cases, so each became a test. A finished run grades now even when its date is today, a rest day is compliant, a partial run is pending rather than half credit, an un-synced day holds pending rather than a false miss, and a synced scheduled day with nothing recorded is a real miss.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_grade_day.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; datetime &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grading.model &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Day, Plan, Actual&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grading.grade &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day, Verdict&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; day&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(plan, actual, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Day(date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2026&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;23&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), plan, actual, is_synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;synced)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_grade_ignores_the_date_field&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # the real &quot;today&quot; guard: two days identical except `on`, one dated today and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # one in the past, must grade the same. This is the test that fails the moment&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # grading reads the calendar again.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    today &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Day(date.today(),     Plan.RUN, Actual.COMPLETED, is_synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    past  &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Day(date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2020&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), Plan.RUN, Actual.COMPLETED, is_synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(today) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(past) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.DONE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_completed_run_grades_immediately&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # a finished run must not sit at pending, and sync state can&apos;t change that&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(day(Plan.RUN, Actual.COMPLETED)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.DONE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(day(Plan.RUN, Actual.COMPLETED, synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;False)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.DONE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_walk_counts_as_done_on_an_easy_day&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # the headline rule, and that it&apos;s scoped to easy days&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(day(Plan.EASY, Actual.WALK)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.DONE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(day(Plan.RUN,  Actual.WALK)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.MISSED&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_rest_day_is_compliant_not_missed&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # rest grades compliant regardless of sync state&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(day(Plan.REST, Actual.NONE)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.COMPLIANT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(day(Plan.REST, Actual.NONE, synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;False)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.COMPLIANT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_partial_run_is_pending_not_half_credit&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(day(Plan.RUN, Actual.PARTIAL)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.PENDING&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_unsynced_day_holds_pending_not_a_false_miss&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(day(Plan.RUN, Actual.NONE, synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;False)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.PENDING&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_synced_scheduled_nothing_is_a_real_miss&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(day(Plan.RUN, Actual.NONE, synced&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Verdict.MISSED&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each test names a case the old date-based logic got wrong. The one that does the real guarding is &lt;code&gt;test_grade_ignores_the_date_field&lt;/code&gt;: it grades two otherwise-identical days, one dated today and one years in the past, and asserts the same verdict, so it fails the moment grading reads the calendar again. The grading is honest now, and that date-independence is the first thing the tests pin down.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The grading reads outcomes correctly. The natural follow-on is letting the coach fold a recovery walk into its read of the week, not just score it correctly, so an easy walk shapes the next day’s recommendation instead of only landing as a green row.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nightlies.apache.org/flink/flink-docs-stable/docs/concepts/time/&quot;&gt;Apache Flink: Timely Stream Processing&lt;/a&gt; — processing time tracks the machine’s wall clock and drifts from the data’s own event time, which is exactly the gap at “today.”&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Decision_table&quot;&gt;Decision table&lt;/a&gt; — a compact mapping from input conditions to actions; the shape behind grading by (plan, actual) with no transitions or stored state.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Single_source_of_truth&quot;&gt;Single source of truth&lt;/a&gt; — each fact has one authoritative home; everything else reads from it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://react.dev/learn/choosing-the-state-structure&quot;&gt;React: Choosing the State Structure&lt;/a&gt; — derive values from the source instead of recomputing them separately so they cannot desync.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot;&gt;Don’t repeat yourself&lt;/a&gt; — a single decision should be expressed in exactly one place.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;fix: outcome-based plan grading + recovery walks count on easy days (0.8.0) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/20b19e05271fb37854b418c80e86a685c6268030&quot;&gt;20b19e0&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;docs: design for training-plan grading fixes (today-pending + walks) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/f93db607bfbc2e9a89ce64d0557213855078c9a1&quot;&gt;f93db60&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Give the agent a tool, not a shell</title><link>https://natejswenson.com/devlog/local-fitness/v0.7.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/local-fitness/v0.7.0/</guid><description>My fitness agent answered a plan question by opening a shell and writing raw SQL. The fix was not a better prompt, it was a missing tool. Here is the full build of the high-level tool that replaced the improvisation: a data model, a per-day grading rule with one honest boundary, the single tool that returns the whole graded plan, and the test that defends it.</description><pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;I asked my fitness agent a simple question: show me my training plan through today. It got there, but first it opened a shell, poked at the database schema, and ran a couple of SQL queries that errored before it finally printed a table. The answer was fine. Everything in front of the answer was noise I never asked for.&lt;/p&gt;
&lt;p&gt;v0.7.0 adds &lt;code&gt;get_training_plan_progress&lt;/code&gt;, which returns the whole plan in one call: every prescribed workout with its verdict (done, missed, pending, or rest), plus days to race, adherence, and a projected finish. Now the agent makes one tool call and hands back a clean table, with no shell and no schema spelunking. I also added a short rule, in the prompt and in the project’s instructions, to prefer the structured tools and never drop to sqlite by hand. The part worth writing about is how to actually build a tool like this so the agent never wants the shell again, and which of those two changes does the real work.&lt;/p&gt;
&lt;h2&gt;When an agent improvises, a tool is missing&lt;/h2&gt;
&lt;p&gt;The shell-poking was not the agent misbehaving. It was the agent routing around a gap. It had no clean way to ask for the graded plan day by day, so it reached for the lowest-level access it did have, raw SQL against the database. An agent dropping to primitives is a signal worth reading literally: the high-level affordance it needed did not exist, so it improvised one badly.&lt;/p&gt;
&lt;p&gt;That maps onto how Anthropic frames agent design. The guidance is to invest in well-designed, high-level tools that return what the caller actually wants, rather than making the model assemble primitives itself (&lt;a href=&quot;https://www.anthropic.com/engineering/writing-tools-for-agents&quot;&gt;Anthropic: Writing tools for AI agents&lt;/a&gt;), and to keep the surrounding system as simple as the task allows (&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt;). The data was already there; only the web view knew how to read it. Giving the agent the same reading as a single tool is what removed the improvisation. The rest of this post is that tool, built end to end.&lt;/p&gt;
&lt;h2&gt;Setup: model the plan and what graded means&lt;/h2&gt;
&lt;p&gt;Before there can be a tool, there has to be a clear data model and a precise definition of the answer. The plan is a list of prescribed workouts keyed by date. Separately, logged workouts record what actually happened. Grading is the join between them. Start with plain, frozen dataclasses and a &lt;code&gt;Verdict&lt;/code&gt; type so the three states are named once and reused everywhere.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/plan.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclasses &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; datetime &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; typing &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Literal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Literal[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;done&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;missed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;pending&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;rest&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@dataclass&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(frozen&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Prescribed&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    day: date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    workout: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;          # human label, e.g. &quot;8 mi easy&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    target_miles: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@dataclass&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(frozen&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Logged&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    day: date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    miles: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;          # what Garmin or a manual entry recorded&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The data layer already knows how to read both lists out of SQLite. The point of this release is that the agent should never have to.&lt;/p&gt;
&lt;h2&gt;Grade each day, with one honest boundary&lt;/h2&gt;
&lt;p&gt;The grading rule is small, and almost all of its value is in one boundary. A rest day owes nothing, so it short-circuits first: a past or current rest day is &lt;code&gt;rest&lt;/code&gt;, a future one is &lt;code&gt;pending&lt;/code&gt;. For a real workout, the past with nothing logged is &lt;code&gt;missed&lt;/code&gt; and the future is &lt;code&gt;pending&lt;/code&gt;. The case that is easy to get wrong is today: a workout prescribed for today with nothing logged yet is still &lt;code&gt;pending&lt;/code&gt;, not &lt;code&gt;missed&lt;/code&gt;, because the day is not over. Grade today as missed and the agent will tell an athlete they failed a workout they still have hours to run.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/plan.py (continued)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; grade_day&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(p: Prescribed, logged: Logged &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None, today: date) -&gt; Verdict:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p.target_miles &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:       &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# rest day: nothing is owed&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;pending&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p.day &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; today &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;rest&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; logged &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; logged.miles &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;done&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p.day &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; today:            &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# today is not over yet, future is not owed yet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;pending&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;missed&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;               # prescribed before today, nothing logged&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keeping &lt;code&gt;today&lt;/code&gt; an explicit argument rather than calling &lt;code&gt;date.today()&lt;/code&gt; inside the function is what makes the boundary testable later. The function is pure, so a test can hand it any reference date.&lt;/p&gt;
&lt;h2&gt;Assemble the plan and expose it as one tool&lt;/h2&gt;
&lt;p&gt;Now wrap the grading in an aggregation that also computes the summary fields the caller wants, then expose the whole thing as a single tool. Adherence is completed over owed workouts, counting only resolved days (graded &lt;code&gt;done&lt;/code&gt; or &lt;code&gt;missed&lt;/code&gt;); rest days and not-yet-due days do not count, so the denominator never includes a workout the athlete has not actually had a chance to finish. The projected finish is a deliberately simple model; the real one can drop in behind the same return shape, which is the point of returning structured data the caller does not have to assemble (&lt;a href=&quot;https://github.com/modelcontextprotocol/python-sdk&quot;&gt;MCP Python SDK&lt;/a&gt;).&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/plan.py (continued)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; project_finish&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(goal_minutes: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, adherence: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Simple stand-in model: missed volume slows you proportionally.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Swap in your real projection without changing the return shape.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; adherence &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; goal_minutes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    penalty &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; adherence) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0.08&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;          # up to ~8% slower at zero adherence&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; round&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(goal_minutes &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; penalty), &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; training_plan_progress&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(plan: list[Prescribed], logs: list[Logged],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                           today: date, goal_minutes: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    by_day &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {l.day: l &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; l &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; logs}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    days, completed, owed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [], &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; plan:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(p, by_day.get(p.day), today)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;done&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;missed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;):   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# only resolved days count&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            owed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            completed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;done&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        days.append({&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: p.day.isoformat(), &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;prescribed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: p.workout,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;                     &quot;verdict&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: verdict})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    adherence &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; round&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(completed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; owed, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; owed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;days&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: days,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;days_to_race&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: (plan[&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;].day &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; today).days,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;adherence&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: adherence,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;projected_finish_minutes&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: project_finish(goal_minutes, adherence),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tool itself is a thin wrapper. It reads the two lists, calls today’s date once, and returns the structured result. I register it with the MCP Python SDK’s &lt;code&gt;FastMCP&lt;/code&gt; (the &lt;code&gt;mcp&lt;/code&gt; package): you create a server, decorate the function with &lt;code&gt;@mcp.tool()&lt;/code&gt;, and &lt;code&gt;FastMCP&lt;/code&gt; derives the tool name from the function name and the description from the docstring, then inspects the type hints for the schema. The agent calls it with no arguments and gets back everything it would otherwise have tried to reconstruct with SQL.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/tools.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; datetime &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.server.fastmcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.plan &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; training_plan_progress&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;mcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@mcp.tool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; get_training_plan_progress&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Return the full training plan with per-day grading and projections.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; training_plan_progress(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        plan&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;db.prescribed_workouts(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        logs&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;db.logged_workouts(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        today&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;date.today(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        goal_minutes&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;db.race_goal_minutes(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __name__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    mcp.run()   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# stdio transport by default; the agent connects to this server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use it: one call, one clean answer&lt;/h2&gt;
&lt;p&gt;Asking the agent for the plan now triggers a single &lt;code&gt;tools/call&lt;/code&gt;, and the tool returns the same structured shape every time. The agent formats that into a table instead of opening a shell.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;days&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;2026-06-19&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;prescribed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;5 mi easy&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,       &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;verdict&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;missed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;2026-06-20&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;prescribed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;8 mi easy&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,       &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;verdict&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;done&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;2026-06-21&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;prescribed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;rest&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,            &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;verdict&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;rest&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;2026-06-22&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;prescribed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;5 mi tempo&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,      &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;verdict&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;pending&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;2026-06-23&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;prescribed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;12 mi long run&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,  &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;verdict&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;pending&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;2026-08-25&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;prescribed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;race day&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,        &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;verdict&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;pending&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;days_to_race&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;64&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;adherence&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0.5&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;projected_finish_minutes&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;214.7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Today’s tempo run reads &lt;code&gt;pending&lt;/code&gt;, not &lt;code&gt;missed&lt;/code&gt;, which is the boundary doing its job, and the rest day reads &lt;code&gt;rest&lt;/code&gt; rather than being scored as a missed workout. Adherence is &lt;code&gt;0.5&lt;/code&gt; because only two days have resolved (one done, one missed); today, the future runs, and the rest day stay out of the denominator. Everything before today is already graded, and the summary fields ride along so the agent never has to total anything itself.&lt;/p&gt;
&lt;h2&gt;Verify the boundary&lt;/h2&gt;
&lt;p&gt;The grading rule is pure, so the test is direct, and the one case worth defending is the today boundary. Pin a reference date and assert each verdict, including that today with nothing logged stays &lt;code&gt;pending&lt;/code&gt; and a past rest day reads &lt;code&gt;rest&lt;/code&gt; rather than &lt;code&gt;missed&lt;/code&gt;. This is the failure mode that would quietly tell an athlete they missed a workout they still had time to run.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_plan.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; datetime &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.plan &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Prescribed, Logged, grade_day&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;TODAY &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2026&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;22&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_logged_is_done&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Prescribed(TODAY, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;5 mi tempo&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;5.0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(p, Logged(TODAY, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;5.1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), TODAY) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;done&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_today_unlogged_is_pending_not_missed&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Prescribed(TODAY, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;5 mi tempo&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;5.0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(p, None, TODAY) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;pending&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   # the day is not over&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_past_unlogged_is_missed&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Prescribed(date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2026&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;21&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;5 mi easy&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;5.0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(p, None, TODAY) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;missed&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_past_rest_day_is_rest_not_missed&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Prescribed(date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2026&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;21&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;rest&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0.0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(p, None, TODAY) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;rest&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   # resting is not failing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_future_is_pending&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Prescribed(date(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2026&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;23&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;12 mi long run&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;12.0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; grade_day(p, None, TODAY) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;pending&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second test fails the moment someone simplifies the boundary to &lt;code&gt;p.day &amp;gt; today&lt;/code&gt;, which is exactly the off-by-one that would mislabel today’s work.&lt;/p&gt;
&lt;h2&gt;The rule is a backstop, not the fix&lt;/h2&gt;
&lt;p&gt;I also added the instruction to prefer structured tools and avoid hand-written SQL, and I want to be honest about what that rule does. It is not the fix. The tool is the fix. A rule asking the model to behave is only reliable when behaving is also the path of least resistance, which is really a context-engineering point: behavior is shaped by what is available and what the instructions say, together (&lt;a href=&quot;https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents&quot;&gt;Anthropic: Effective context engineering for AI agents&lt;/a&gt;). With the tool in place, the rule keeps the agent honest on the days it gets clever and wants to go spelunking anyway. Without the tool, the same rule is just a sternly worded request the model can ignore the moment SQL looks faster. When you catch an agent improvising with low-level access, the durable move is to build the capability it was missing and then point the instructions at it.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The brief’s cross-model A/B check turned out to be flaky in a way that predates this release. Making that harness reliable is the next cleanup, so prompt changes stay cheap to verify.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/writing-tools-for-agents&quot;&gt;Anthropic: Writing tools for AI agents&lt;/a&gt; — invest in high-level tools that return what the caller wants.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt; — keep the system as simple as the task allows.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents&quot;&gt;Anthropic: Effective context engineering for AI agents&lt;/a&gt; — behavior is shaped by tools and instructions together.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/modelcontextprotocol/python-sdk&quot;&gt;MCP Python SDK&lt;/a&gt; — define a tool that returns structured data the caller does not have to assemble.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat: get_training_plan_progress tool + prefer-structured-tools nudge (0.7.0) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/09827e514470a7ea1643a0498b536efc50434a60&quot;&gt;09827e5&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;docs: design for clean fitness Q&amp;amp;A (plan-progress tool + presentation contract) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/fd92067a40026f09689d6bd2cfc6c4f2eeec3a4b&quot;&gt;fd92067&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Cohesion is leverage: pulling a drifting card family onto one base</title><link>https://natejswenson.com/devlog/ghostwriter/v0.4.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/ghostwriter/v0.4.0/</guid><description>Ghostwriter&apos;s image cards had drifted into separate looks. Redesigning them onto one shared base isn&apos;t just about brand; the base is what makes the next card type cheap to add and structurally unable to drift. Here&apos;s the full build, from tokens to a rendered PNG to the check that keeps the family honest.</description><pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;v0.4.0 pulled Ghostwriter’s image cards back into one family. They had drifted, each type growing its own look over time, so a feed of them no longer read as one brand. Now they sit on the same dark canvas with the same restraint on color: the date card as an ADMIT-ONE ticket, the flow card threaded on a numbered spine, the ramp card as an actual analytics chart with a trend line, the STEM card stripped of its confetti but keeping the chunky blocks. Two new cards landed too: a code card that renders a snippet as a terminal session with line numbers and theme colors, and a Claude Code card that shows a session transcript. The carousel got rebuilt as a portrait 4:5 deck with a progress bar and counter on every slide. I ran it all through a quality gate before merging.&lt;/p&gt;
&lt;p&gt;The feature is “the cards look like one product again.” The part worth writing about is the structure underneath it, because a shared base does more than unify the brand: it makes the next card type cheap to add and makes drift something the system rejects rather than something I have to police. Here’s the end-to-end build.&lt;/p&gt;
&lt;h2&gt;Setup: a base that owns every shared decision&lt;/h2&gt;
&lt;p&gt;The obvious justification for unifying the cards is “they should look like one brand.” That’s true, and it undersells it. The Nielsen Norman Group defines a design system as a set of standards that manages design at scale by reducing redundancy and creating a shared language and visual consistency (&lt;a href=&quot;https://www.nngroup.com/articles/design-systems-101/&quot;&gt;NN/g: Design Systems 101&lt;/a&gt;). The operative phrase is &lt;em&gt;reducing redundancy&lt;/em&gt;. When every card reinvents its own canvas, spacing, and color choices, each new card type pays the full cost of those decisions again, and the whole set drifts a little further apart every time.&lt;/p&gt;
&lt;p&gt;The fix starts with one source of truth. I put the family’s shared values in CSS custom properties, which let a single declaration cascade to every element that reads it (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties&quot;&gt;MDN: Using CSS custom properties&lt;/a&gt;). The &lt;code&gt;.card&lt;/code&gt; base then consumes those tokens and claims the canvas, the surface, the type, and the spacing once, for everyone.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* cards.css */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:root&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  /* One source of truth for the whole family. Cards read these, never raw values. */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --card-w&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1080px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --card-aspect&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 4&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; / &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* portrait, claimed once for every card */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --card-bg&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #0a0a0b&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --card-fg&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #ededed&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --card-accent&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #e6b450&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --card-pad&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 64px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --card-font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; ui-monospace&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;SF Mono&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;monospace&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* Every card inherits the canvas, surface, type, and spacing from here. */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  width&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--card-w&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  aspect-ratio&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--card-aspect&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  background&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--card-bg&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--card-fg&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  font-family&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--card-font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  padding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--card-pad&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  box-sizing&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; border-box&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  flex-direction&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; column&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pulling the family onto this base flips the cost curve. The shared decisions get made exactly once, so a new card inherits the look for free and only has to bring what’s actually unique about its shape.&lt;/p&gt;
&lt;h2&gt;Build: a card type is just its own deltas&lt;/h2&gt;
&lt;p&gt;With the base carrying the canvas, a concrete card type becomes small. I followed the BEM convention here: &lt;code&gt;.card&lt;/code&gt; is the block, and each type is a modifier like &lt;code&gt;.card--ramp&lt;/code&gt; that adjusts the block without redefining it (&lt;a href=&quot;https://getbem.com/naming/&quot;&gt;BEM: Naming convention&lt;/a&gt;). The rule I held to is that a modifier may bring its own layout, but it reuses the family’s tokens rather than introducing new raw values.&lt;/p&gt;
&lt;p&gt;Here’s the ramp card, the analytics-chart type, in full. It does not restate the background, the size, the font, or the accent color, because those already came down from the base.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* The ramp card: bars that grow toward a trend. Only its own deltas live here. */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card--ramp&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  justify-content&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex-end&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;       &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* anchor the chart to the bottom of the canvas */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card--ramp&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; .card__bars&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex-end&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  gap&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 16px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  height&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 60%&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card--ramp&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; .card__bar&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  flex&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  background&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--card-accent&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* reuse the family accent; never pick a new color here */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  border-radius&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 6px&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 6px&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is the entire cost of a new card type now: roughly a dozen lines describing one shape. The code card and the Claude Code card landed the same way, each as a thin modifier over the same base, which is why two new types shipped in one release without the set growing inconsistent.&lt;/p&gt;
&lt;h2&gt;Use it: compose base plus modifier, then render to PNG&lt;/h2&gt;
&lt;p&gt;A card is authored as the base class plus its modifier, so the markup reads as “a card, of the ramp kind.” The HTML never touches colors or sizes; it only fills in content. &lt;code&gt;.card__title&lt;/code&gt; needs little of its own; it inherits color and font-family straight from the base, so a type rule only has to set what’s actually distinct, like size or margin.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card card--ramp&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card__title&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Training load is trending up&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card__bars&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card__bar&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height: 35%&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card__bar&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height: 50%&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card__bar&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height: 48%&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card__bar&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height: 70%&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card__bar&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height: 88%&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ghostwriter ships these as images, so the last step is rasterizing that HTML to a PNG at its true pixel size. A headless Chromium does the rendering (a one-time &lt;code&gt;pip install playwright &amp;amp;&amp;amp; playwright install chromium&lt;/code&gt;), and I clip the screenshot to the &lt;code&gt;.card&lt;/code&gt; element so the output is exactly the card box, not the page around it.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# render.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; playwright.sync_api &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;CSS_PATH &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(__file__).parent &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;cards.css&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # resolve next to this file, not the CWD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; render_card&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(card_html: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, out: Path, width: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1080&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Rasterize one card&apos;s HTML to a PNG at the family&apos;s true pixel size.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    Renders exactly one card per call: the markup should contain a single&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    `.card`, and the screenshot is clipped to it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    height &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(width &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 5&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 4&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the base&apos;s 4:5 aspect, mirrored on the viewport&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    css &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; CSS_PATH.read_text()   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# inline it: set_content has no base URL, so a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    doc &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (                      &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# relative &amp;#x3C;link href=&quot;cards.css&quot;&gt; would never resolve&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &apos;&amp;#x3C;!doctype html&gt;&amp;#x3C;meta charset=&quot;utf-8&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&amp;#x3C;style&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;css&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&amp;#x3C;/style&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;card_html&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        browser &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p.chromium.launch()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; browser.new_page(viewport&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: width, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: height})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page.set_content(doc, wait_until&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;load&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # .first keeps this from raising strict-mode if the markup ever holds more&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # than one .card; one card per call is the contract, so .first is the box we want.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page.locator(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.card&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).first.screenshot(path&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(out))  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# clip to the card, not the page&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        browser.close()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# One card per call; the base owns the dimensions, so there&apos;s no per-type sizing here.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;card_html &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&amp;#x3C;article class=&quot;card card--ramp&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  &amp;#x3C;h1 class=&quot;card__title&quot;&gt;Training load is trending up&amp;#x3C;/h1&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  &amp;#x3C;div class=&quot;card__bars&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &amp;#x3C;div class=&quot;card__bar&quot; style=&quot;height: 35%&quot;&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &amp;#x3C;div class=&quot;card__bar&quot; style=&quot;height: 50%&quot;&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &amp;#x3C;div class=&quot;card__bar&quot; style=&quot;height: 70%&quot;&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &amp;#x3C;div class=&quot;card__bar&quot; style=&quot;height: 88%&quot;&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  &amp;#x3C;/div&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&amp;#x3C;/article&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;render_card(card_html, Path(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp.png&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every card type goes through this one function. Because the base owns the dimensions, the renderer never needs per-type sizing logic; a new card type plugs into the exact same call.&lt;/p&gt;
&lt;h2&gt;Verify: the base owns the family, so a type can’t drift&lt;/h2&gt;
&lt;p&gt;The reason to do this work isn’t only that the cards look unified today. It’s that consistency stops depending on me remembering to be consistent. The weak version of that promise is a comment that says “don’t set colors in a card modifier.” A comment survives exactly until the next person, or the next hurried version of me, ignores it.&lt;/p&gt;
&lt;p&gt;The durable version makes drift fail a check. The contract is simple: family-owned properties such as the background, dimensions, type, and base color belong to &lt;code&gt;.card&lt;/code&gt; and the tokens; a &lt;code&gt;.card--*&lt;/code&gt; modifier that redeclares any of them is drifting by definition. A small test parses the stylesheet and enforces exactly that.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_card_family.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Resolve relative to this test file, and assume flat (non-nested) CSS: the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# rule regex below splits on top-level braces, so a modifier buried inside an&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# @media {...} block would be missed. Keep the family rules un-nested.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;CSS &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (Path(__file__).parent.parent &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;cards.css&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).read_text()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Decisions the base alone is allowed to make for the whole family. These are&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# shorthands; the check below also matches their longhands by prefix, so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# `background-color` or `inline-size` can&apos;t slip past the shorthand list.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;FAMILY_OWNED &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;background&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;background-image&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;width&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;inline-size&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;block-size&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;aspect-ratio&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;font-family&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;color&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; _is_owned&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(prop):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(prop &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; owned &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;or&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; prop.startswith(owned &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;-&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; owned &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FAMILY_OWNED)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; _rules&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; selector, body &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.findall(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&quot;([^&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;]+)\{([^&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;]*)\}&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, CSS):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # Strip comments before splitting: a `;` inside a /* ... */ comment would&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # otherwise mangle the property name of the declaration that follows it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        body &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.sub(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&quot;/\*.*?\*/&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, body, flags&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;re.S)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        props &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {p.split(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;:&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;].strip() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; body.split(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;:&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        yield&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; selector.strip(), props&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_modifiers_never_redeclare_family_props&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;A card type may define its own layout; it may not redefine the canvas.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; selector, props &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _rules():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # Only judge rules whose SUBJECT is the card box itself: `.card--x`,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # `.card.card--x`, and pseudo/chained subjects like `.card--ticket::before`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # or `.card--ticket:hover` (a perforation pseudo-element is a natural place&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # to sneak in a background). A descendant like `.card--ramp .card__bar` is a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # separate, space-delimited element legitimately reading a family prop, so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # skip it; otherwise the bars&apos; own `background` would trip the guard.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.search(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&quot;\.card--[\w&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;]+(?:::?[\w&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;]+|\.[\w&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;]+|\[[^\]]*\])*\s*$&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, selector):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            continue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        drift &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; props &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _is_owned(p)}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        assert&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; drift, (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;selector&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; redeclares family-owned &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{sorted&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(drift)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;; &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;inherit it from .card instead of overriding it.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the principle of least privilege applied to CSS: a modifier gets only the power to describe its own shape, not the power to redefine the family. The moment someone adds &lt;code&gt;background-color: #112233&lt;/code&gt; to a card type to make it “pop,” the test goes red and points at the selector; the prefix match means the longhand is caught just like the &lt;code&gt;background&lt;/code&gt; shorthand, and the subject match catches it on a pseudo-element too, so painting a background through &lt;code&gt;.card--ticket::before&lt;/code&gt; doesn’t slip past. The base owns the look, and the check makes that ownership real instead of aspirational.&lt;/p&gt;
&lt;h2&gt;The carousel followed the same discipline&lt;/h2&gt;
&lt;p&gt;The carousel got the most visible work, and I did the unglamorous thing first: I read what the format rewards before restyling. NN/g’s first usability heuristic is visibility of system status: a design should always keep users informed about what is going on, through appropriate feedback within reasonable time (&lt;a href=&quot;https://www.nngroup.com/articles/visibility-system-status/&quot;&gt;NN/g: Visibility of System Status&lt;/a&gt;). For a multi-slide deck, “what is going on” is mostly “where am I in the sequence,” and NN/g’s guidance on progress indicators makes that concrete: when a percentage isn’t meaningful, show the number of steps and indicate the current one so people can form an estimate of what’s left (&lt;a href=&quot;https://www.nngroup.com/articles/progress-indicators/&quot;&gt;NN/g: Progress Indicators&lt;/a&gt;). So every slide now carries a progress bar and a counter that say where you are in the deck, the cover stands apart to set the frame, and the deck ends on one clear ask. The slides are still cards on the same base; the progress affordance is one more modifier, not a separate styling universe.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The portrait sizing and the progress affordance are worth carrying back into the single-image formats, so the whole output reads as one system rather than a carousel plus some loose cards. Because the family is consistent now, that kind of change propagates from the base and the tokens instead of being reapplied card by card, and the family test keeps any new format from quietly forking the look.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nngroup.com/articles/design-systems-101/&quot;&gt;Nielsen Norman Group: Design Systems 101&lt;/a&gt; — a design system reduces redundancy and creates a shared language and visual consistency.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties&quot;&gt;MDN: Using CSS custom properties&lt;/a&gt; — one declaration cascades to every element that reads the variable, giving the family a single source of truth.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://getbem.com/naming/&quot;&gt;BEM: Naming convention&lt;/a&gt; — block plus modifier, so a card type adjusts the base block without redefining it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nngroup.com/articles/visibility-system-status/&quot;&gt;Nielsen Norman Group: Visibility of System Status&lt;/a&gt; — keep users informed about what is going on through appropriate feedback within reasonable time.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nngroup.com/articles/progress-indicators/&quot;&gt;Nielsen Norman Group: Progress Indicators&lt;/a&gt; — when a percentage isn’t meaningful, show the number of steps and indicate the current one so users can estimate what remains.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ghostwriter 0.4.0: card-family redesign, code/claude cards, portrait carousel (#12) (&lt;a href=&quot;https://github.com/natejswenson/claude-skills/commit/206e3f8cde9bd5029c5ba4b52885730b9ded1f17&quot;&gt;206e3f8&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;reusable release + per-skill callers, shared skill scorer, devlog tests (#5) (&lt;a href=&quot;https://github.com/natejswenson/claude-skills/commit/c3870d67ccb7b63b2182ec94635e32d55085da01&quot;&gt;c3870d6&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>A benchmark you&apos;ll actually run, and a retry that stopped redoing everything</title><link>https://natejswenson.com/devlog/resume/v0.2.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/resume/v0.2.0/</guid><description>The resume skill got a benchmark cheap and stable enough to run on every change, and a retry path roughly seven times faster because it only redoes the part that changed. Here is the full build: fixtures and a rubric, a judge that runs through the Claude CLI, a gate that watches only the treatment, and an incremental retry.</description><pubDate>Sun, 21 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;The resume skill tailors a résumé to a job, and v0.2.0 gives it two things it was missing: a benchmark you can trust, and a retry that stops wasting time. &lt;code&gt;npm run benchmark&lt;/code&gt; runs the pipeline against seven real jobs and scores the output, with the judge running through the Claude CLI so it’s intended to run under a Pro or Max subscription rather than a metered per-run API key, and the gate looking only at the treatment so a noisy baseline can’t move the verdict. (More on that “intended to” below: programmatic CLI use can still bill per token, so it’s worth confirming.) Separately, the slow retry path went from about 39 seconds to about 5.3 seconds by retrying only the summary instead of the whole thing.&lt;/p&gt;
&lt;p&gt;Both changes are the same idea wearing two hats: make the feedback loop cheap enough that you actually use it. The part worth writing about is how the benchmark is built so it survives daily use, and how the retry got fast without changing its output. Here is the end-to-end build, from the fixtures and rubric through the gate that fails the build on a regression.&lt;/p&gt;
&lt;h2&gt;Setup: the fixtures and the rubric&lt;/h2&gt;
&lt;p&gt;A benchmark needs two things before any code runs: a set of cases that look like real work, and a definition of what “good” means on each case. Anthropic’s guidance is to build evals around what actually matters and to keep the cases representative rather than convenient (&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt;). For this skill a case is one real job description paired with the base résumé, and “good” is a small scoring rubric the judge applies to the tailored output.&lt;/p&gt;
&lt;p&gt;The fixtures live on disk so they’re easy to add to and easy to read. Each one is a directory with the job and the résumé it should be tailored from.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# benchmark/fixtures.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclasses &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@dataclass&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(frozen&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Fixture&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    key: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    job: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;       # the job description text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    resume: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # the base résumé to tailor from&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;FIXTURE_DIR &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(__file__).parent &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;fixtures&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; load_fixtures&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; list[Fixture]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Each fixture is a real job+resume pair under benchmark/fixtures/&amp;#x3C;key&gt;/.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    fixtures &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; d &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; sorted&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FIXTURE_DIR.iterdir() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p.is_dir()):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        fixtures.append(Fixture(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            key&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;d.name,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            job&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(d &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;job.md&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).read_text(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            resume&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(d &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;resume.md&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).read_text(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        ))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fixtures&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rubric is the part that turns a vibe into a number. Spelling the dimensions out, and weighting them, is what makes two runs comparable. Truthfulness carries real weight here because a résumé that invents experience is worse than one that’s merely generic.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# benchmark/rubric.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;RUBRIC &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;Score the TAILORED résumé against the JOB, 1-5 on each criterion:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;- relevance:    do the bullets surface the experience the job asks for?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;- specificity:  concrete tools and outcomes, not generic filler?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;- truthfulness: is every claim traceable to the base résumé (nothing invented)?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;- coverage:     are the job&apos;s must-have requirements addressed?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;Return ONLY JSON: {&quot;relevance&quot;:N,&quot;specificity&quot;:N,&quot;truthfulness&quot;:N,&quot;coverage&quot;:N}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;WEIGHTS &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;relevance&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0.35&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;specificity&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0.20&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;truthfulness&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0.30&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;coverage&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0.15&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; weighted_score&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(scores: dict[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Collapse the rubric dimensions into one 1-5 number.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; sum&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(scores[dim] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; w &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dim, w &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; WEIGHTS.items())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build: a judge that runs through the Claude CLI&lt;/h2&gt;
&lt;p&gt;The judge is an LLM grading the output against the rubric, which is a standard way to score open-ended text that has no single correct answer (&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt;). The design choice that matters is where it runs. Calling the metered API for every fixture on every change adds a small bill to a thing I want to run constantly, and a cost per run is exactly the friction that turns “run the benchmark” into “skip the benchmark this time.” Running the judge through the Claude CLI is intended to run under a Claude Pro or Max subscription rather than a metered per-run API key.&lt;/p&gt;
&lt;p&gt;Treat that as the goal, not a guarantee. Programmatic &lt;code&gt;claude -p&lt;/code&gt; use has documented cases of per-token billing even with an active subscription over OAuth and no &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; set (&lt;a href=&quot;https://github.com/anthropics/claude-code/issues/43333&quot;&gt;claude-code #43333&lt;/a&gt;, &lt;a href=&quot;https://github.com/anthropics/claude-code/issues/37686&quot;&gt;#37686&lt;/a&gt;); and if the CLI is authenticated with an &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; it is metered for certain. Before assuming a run costs nothing, confirm it: &lt;code&gt;claude -p --output-format json&lt;/code&gt; is the documented programmatic path, and its envelope reports a &lt;code&gt;total_cost_usd&lt;/code&gt; you can read. That JSON envelope still puts the model’s reply in a free-text &lt;code&gt;.result&lt;/code&gt; field, so the defensive extraction below applies either way.&lt;/p&gt;
&lt;p&gt;The other thing the CLI forces you to handle is the output. &lt;code&gt;claude -p&lt;/code&gt; returns the model’s plain text reply, not guaranteed-bare JSON: it may wrap the object in a fenced JSON code block or a sentence of preamble, so &lt;code&gt;json.loads(proc.stdout)&lt;/code&gt; will intermittently raise. So the judge extracts the JSON defensively, pulling the outermost &lt;code&gt;{...}&lt;/code&gt; span out of whatever the model returned before parsing it.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# benchmark/judge.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; json, re, subprocess&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; benchmark.rubric &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; RUBRIC, WEIGHTS, weighted_score&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; _extract_scores&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(text: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;`claude -p` returns free text, not guaranteed-bare JSON: the reply may carry&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    a fenced JSON block or a line of preamble. Pull out the outermost {...} span and parse it.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    match &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.search(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&quot;\{.*\}&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, text, re.DOTALL)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; match:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        raise&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; ValueError&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;no JSON object in judge reply: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;!r&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    scores &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; json.loads(match.group(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    missing &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; WEIGHTS.keys() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; scores.keys()   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# every rubric dimension must be present&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; missing:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        raise&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; ValueError&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;judge reply missing rubric keys &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{sorted&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(missing)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;scores&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;!r&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; scores&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; judge&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(job: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, tailored: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Score one tailored résumé via the Claude CLI. Intended to run under a Claude&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    Pro/Max subscription, but programmatic -p use can still bill per token; confirm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    actual cost with `--output-format json`&apos;s total_cost_usd rather than assuming zero.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    prompt &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;RUBRIC&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;JOB:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;job&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;TAILORED RESUME:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;tailored&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    proc &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; subprocess.run(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;claude&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;-p&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, prompt],          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# CLI invocation, not the metered API&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        capture_output&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True, text&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True, check&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    scores &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _extract_scores(proc.stdout)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# extract JSON defensively; the reply isn&apos;t bare JSON&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; weighted_score(scores)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The runner ties it together: load the fixtures, tailor each one, judge the result, and report the mean. The thing being measured is the tailored output, the treatment, and nothing else is scored.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# benchmark/run.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; statistics&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sys&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; benchmark.fixtures &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_fixtures&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; benchmark.judge &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; judge&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resume.pipeline &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tailor        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the thing under test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;THRESHOLD &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 3.5&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;                            # minimum acceptable mean treatment score (1-5)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    scores &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fx &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; load_fixtures():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        tailored &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tailor(fx.resume, fx.job).document   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# treatment = the tailored output&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        score &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; judge(fx.job, tailored)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        scores.append(score)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;        print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;fx.key&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&amp;#x3C;24&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;score&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:.2f&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    mean &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; statistics.fmean(scores)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;treatment mean: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;mean&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:.2f&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; over &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(scores)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; fixtures&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Gate on the treatment, not the delta&lt;/h2&gt;
&lt;p&gt;The tempting design is to also tailor with the feature off, score that baseline, and gate on the difference. It reads as more rigorous because it’s a controlled comparison. The problem is that the baseline score is itself a judge call, and a judge call has run-to-run variance. A lucky baseline draw can make a flat change look like a win, and an unlucky one can fail a change that’s actually fine. The verdict ends up partly determined by noise in a number I don’t even care about.&lt;/p&gt;
&lt;p&gt;So the gate watches only the treatment, against an absolute threshold. The baseline is still worth logging for context, but it doesn’t get a vote on whether the build passes. Removing the baseline’s variance from the decision is what keeps the gate from crying wolf, and a gate that cries wolf gets ignored, which is the same as having no gate. The whole point of an eval is the repeatable read that gates the next change, not the one-time score (&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Build: a retry that redoes only what changed&lt;/h2&gt;
&lt;p&gt;The pipeline has two stages. Rewriting each role’s bullets is the slow, expensive part; writing the summary off those bullets is quick. The old retry reran both, which meant a wrong summary cost a full pipeline run, and it sometimes emitted no-op bullets that padded the résumé with lines that said nothing. Retrying only the summary takes the slow case from about 39 seconds to about 5.3 seconds for the same output.&lt;/p&gt;
&lt;p&gt;This is incremental computation in miniature: when one input changes, recompute the part that depends on it and reuse the rest instead of rerunning the whole pipeline (&lt;a href=&quot;https://en.wikipedia.org/wiki/Incremental_computing&quot;&gt;Incremental computing&lt;/a&gt;). To make the reuse possible, &lt;code&gt;tailor&lt;/code&gt; returns its intermediate result, the bullets, rather than only the assembled document, so a retry can hand them straight back.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# resume/pipeline.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclasses &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Tailored&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    bullets: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    summary: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    document: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; tailor&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(resume: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, job: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; Tailored:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    bullets &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; rewrite_bullets(resume, job)    &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ~34s: rewrites every role&apos;s bullets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    bullets &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; bullets &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b.strip()]   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# drop no-op bullets instead of padding&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    summary &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; write_summary(bullets, job)     &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ~5s: a short summary derived from the bullets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Tailored(bullets, summary, assemble(bullets, summary))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; retry_summary&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(prev: Tailored, job: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; Tailored:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;The summary was the only thing wrong. Reuse the expensive bullets verbatim.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    summary &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; write_summary(prev.bullets, job)          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# recompute only the cheap stage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Tailored(prev.bullets, summary, assemble(prev.bullets, summary))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rewrite_bullets&lt;/code&gt;, &lt;code&gt;write_summary&lt;/code&gt;, &lt;code&gt;assemble&lt;/code&gt;, and &lt;code&gt;write_pdf&lt;/code&gt; are your own pipeline stages here, treated as black boxes; substitute whatever your skill already does to produce bullets, a summary, an assembled document, and a PDF. And for &lt;code&gt;python -m benchmark.run&lt;/code&gt; to resolve these imports, &lt;code&gt;benchmark/&lt;/code&gt; and &lt;code&gt;resume/&lt;/code&gt; both need to be importable packages from the repo root, each with an &lt;code&gt;__init__.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The bullets are the memoized expensive result. A retry that recomputes them from scratch isn’t a retry, it’s a fresh run wearing a retry label.&lt;/p&gt;
&lt;h2&gt;Use it&lt;/h2&gt;
&lt;p&gt;The benchmark gets an npm entry point (&lt;code&gt;&amp;quot;benchmark&amp;quot;: &amp;quot;python -m benchmark.run&amp;quot;&lt;/code&gt;) so it sits next to the rest of the project’s scripts and runs the same command in CI. The subscription auth doesn’t carry into CI, though: a runner has no interactive Pro/Max login, so in practice CI authenticates the CLI with an &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; secret (and accepts that those runs are metered) or with a long-lived OAuth token. Local runs lean on the subscription; CI pays the per-token rate, so size the fixture set with that in mind. Running &lt;code&gt;npm run benchmark&lt;/code&gt; prints a line per fixture and the mean, then a PASS/FAIL on the threshold:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;backend-platform-sre     4.10&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;data-platform-eng        3.70&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;staff-devops             4.25&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;treatment mean: 3.95 over 7 fixtures&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;PASS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The retry is wired in at the call site. Tailor once, check the cheap stage, and if only the summary is off, redo just that.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# resume/cli.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; summary_is_acceptable&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(summary: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Reject empties, stubs, and obviously-truncated output; otherwise keep it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(summary.strip()) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;TODO&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; summary &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(summary) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 40&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;result &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tailor(resume, job)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; summary_is_acceptable(result.summary):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    result &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; retry_summary(result, job)   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ~5.3s, reuses the bullets from the first run&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;write_pdf(result.document)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Verify: watch the gate fail&lt;/h2&gt;
&lt;p&gt;The threshold check is factored into a &lt;code&gt;gate&lt;/code&gt; function so a test can call it directly, and &lt;code&gt;main&lt;/code&gt; wires it to the process exit code. A mean below the line makes &lt;code&gt;gate&lt;/code&gt; return &lt;code&gt;False&lt;/code&gt;, which exits non-zero, which fails &lt;code&gt;npm run benchmark&lt;/code&gt; and the CI step that calls it.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# benchmark/run.py (same file, continued)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; gate&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(mean: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;The threshold check, factored out so a test can assert on it directly.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mean &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; THRESHOLD:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;        print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;FAIL: treatment &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;mean&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:.2f&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &amp;#x3C; threshold &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;THRESHOLD&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, file&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;sys.stderr)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; False&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;PASS&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; True&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __name__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    sys.exit(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; gate(run()) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)      &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# non-zero exit fails the build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The behavior worth pinning is that a regression actually fails, not just lowers a number nobody reads. Force the judge to return a regressed score and assert the gate trips. Because the gate only reads the treatment, the test needs no baseline; everything else that’s slow or non-deterministic gets stubbed (the fixtures, &lt;code&gt;tailor&lt;/code&gt;, and the judge) so the test exercises only the gate’s decision.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_gate.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; benchmark.run &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; run&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; benchmark.fixtures &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Fixture&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resume.pipeline &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Tailored&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_gate_fails_when_treatment_regresses&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(monkeypatch):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Stub the slow/non-deterministic pieces so only the gate logic runs.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setattr(run, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;load_fixtures&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                        lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [Fixture(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fx&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;job&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;resume&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; i &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; range&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setattr(run, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;tailor&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resume, job: Tailored([], &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setattr(run, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;judge&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; job, out: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2.0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# simulate a quality regression&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    mean &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; run.run()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mean &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; run.THRESHOLD     &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# below the line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; run.gate(mean) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; False  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# so the gate fails the build (main() exits non-zero)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This fails the moment the treatment drops below the threshold, which is the whole job: turn a quiet quality slip into a red build before it ships.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;Leaning on the benchmark to catch quality regressions automatically, and using its per-stage timings to justify the next round of speed work rather than guessing where the time goes. That’s the same discipline as profiling before optimizing, pointed at a model pipeline instead of a tight loop (&lt;a href=&quot;https://en.wikipedia.org/wiki/Program_optimization&quot;&gt;Program optimization&lt;/a&gt;). The 39-to-5.3 win came from knowing which stage held the time, and the next one will come the same way.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt; — build evals around what matters, grade open-ended output with an LLM judge, and run them in the loop.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Incremental_computing&quot;&gt;Incremental computing&lt;/a&gt; — recompute only what changed and reuse the rest instead of rerunning the whole pipeline.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Program_optimization&quot;&gt;Program optimization (Knuth on premature optimization)&lt;/a&gt; — measure before optimizing; let data point at the bottleneck.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat(resume): accuracy + speed benchmark + pipeline improvements (#11) (&lt;a href=&quot;https://github.com/natejswenson/claude-skills/commit/126251974267ccef3e2b4be72d89589705608c1a&quot;&gt;1262519&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>The brief got ~2.5-3x faster, and the fancy idea lost to a measurement</title><link>https://natejswenson.com/devlog/local-fitness/v0.6.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/local-fitness/v0.6.0/</guid><description>The daily brief dropped from about four minutes to roughly 80 seconds at equal-or-better quality, about 2.5-3x. The parallel-fan-out plan I designed lost to a profiler, the real lever was one setting, and less model reasoning made the output better, not worse. Here is the full build: profile the phases, dial reasoning effort to low, render tables in code, and confirm with a blind A/B.</description><pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;The daily brief took about four minutes to write, and that is a number you feel every morning. v0.6.0 takes it to roughly 80 seconds with equal-or-better quality, about 2.5-3x faster. The change was two small moves: dial the model’s reasoning effort down for this output-bound task, and move table formatting out of the model and into code. The part worth writing about is how I picked those moves, because almost every step contradicted what I expected. Here is the end-to-end build: profile where the time goes, set the effort, render tables deterministically, generate a brief through the faster path, and verify the quality with a blind A/B.&lt;/p&gt;
&lt;h2&gt;Profile before you optimize&lt;/h2&gt;
&lt;p&gt;The obvious fix looked like parallelism: split the one big generation into a fan-out and compose the cards concurrently. I designed it, ran the design through a quality gate, and then measured before building anything. The measurement killed it. Concurrent calls only ran 1.44x faster at three-wide, under a bar I had set in advance, so fan-out would have added a pile of moving parts to buy almost nothing. This is Knuth’s old warning with a fresh coat of paint: profile first, because the small efficiencies are not where the time is (&lt;a href=&quot;https://en.wikipedia.org/wiki/Program_optimization&quot;&gt;Program optimization&lt;/a&gt;). It rhymes with Anthropic’s advice for agent systems too, find the simplest thing that works and only add complexity when it demonstrably earns its keep (&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;So instead of building the fan-out, I instrumented a real brief to see where the wall-clock actually went. A phase timer around each step, plus the token usage off the response, is enough to find the bottleneck. This assumes the Anthropic SDK is installed (&lt;code&gt;pip install anthropic&lt;/code&gt;) with &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; set in your environment.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; contextlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; contextmanager&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.brief &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; generate_brief, brief_text  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the Claude call (built below)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; PhaseTimer&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Accumulate wall-clock per named phase so you can see the split.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; __init__&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.phases: dict[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    @contextmanager&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; phase&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self, name: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        start &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; time.perf_counter()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        try&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            yield&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        finally&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.phases[name] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.phases.get(name, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0.0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (time.perf_counter() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; start)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; report&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        total &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; sum&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.phases.values()) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;or&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; name, secs &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; sorted&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.phases.items(), key&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; kv: &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;kv[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;            print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:16&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;secs&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:7.2f&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;s  &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;secs &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; total&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:5.1%&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;timer &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; PhaseTimer()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; timer.phase(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;db_snapshot&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    snapshot &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.snapshot_for(None)        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# most recent day&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; timer.phase(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;model_generate&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    message &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; generate_brief(snapshot)      &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# one Claude call&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;timer.report()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Reasoning is billed in output tokens but never shown to the reader. Compare the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# total output against the visible brief to see how much was hidden thinking.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;visible &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; brief_text(message)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;output tokens:&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, message.usage.output_tokens)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;visible chars:&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(visible))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result was lopsided. The single model-generation phase was essentially the whole runtime, and most of the output tokens were reasoning the reader never sees. The database tools, the part I’d have bet on, were a rounding error in milliseconds. The bottleneck wasn’t the plumbing, it was the model thinking far more than this task needed.&lt;/p&gt;
&lt;h2&gt;The lever was reasoning effort, set low&lt;/h2&gt;
&lt;p&gt;The lever that worked wasn’t the one I reached for. On Opus 4.8 a fixed thinking budget (&lt;code&gt;budget_tokens&lt;/code&gt;) is rejected with a 400; thinking is adaptive-only, and depth is controlled by &lt;code&gt;output_config.effort&lt;/code&gt;. So the lever is effort, not a token budget. Effort controls how much the model thinks before it answers, and the default is high (&lt;a href=&quot;https://platform.claude.com/docs/en/build-with-claude/effort&quot;&gt;Claude: the effort parameter&lt;/a&gt;). A daily brief is output-bound: the hard part is writing well, not reasoning deeply, so high effort spends tokens on hidden deliberation that the brief doesn’t benefit from. Dropping it to low cut the brief to about 80 to 95 seconds.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; anthropic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;client &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; anthropic.Anthropic()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;COACH_SYSTEM &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;You are my training coach. Write a short, specific daily brief.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; generate_brief&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(snapshot: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, effort: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;low&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; anthropic.types.Message:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;One model call, reasoning effort dialed to LOW for the daily path.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    effort defaults to &quot;high&quot;; &quot;low&quot; spends fewer tokens on hidden reasoning and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    more on the brief itself. It&apos;s a parameter, not a prompt trick, so the change&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    is one line and is easy to sweep in the A/B below.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    max_tokens is set high on purpose: on Opus 4.8 reasoning is billed as output&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    tokens and counts against this ceiling. At high effort the model can spend&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    thousands of tokens thinking before it writes a word, so a tight ceiling&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    would truncate the brief mid-thought (or before it starts) and come back&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    with stop_reason=&quot;max_tokens&quot;. The headroom lets the visible brief survive&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    even a large high-effort thinking pass.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; client.messages.create(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        model&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;claude-opus-4-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        max_tokens&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;16000&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        thinking&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;adaptive&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        output_config&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;effort&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: effort},   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the lever that moved the needle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        system&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;COACH_SYSTEM,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        messages&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Write today&apos;s brief:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;snapshot&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; brief_text&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(message: anthropic.types.Message) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;The visible prose, with thinking blocks left out.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    Guard against a truncated generation: if the model hit the token ceiling&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    (stop_reason=&quot;max_tokens&quot;), the visible text is partial or empty, so refuse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    it here rather than let a half-written brief reach the saved output.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; message.stop_reason &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;max_tokens&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        raise&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; RuntimeError&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;brief truncated at max_tokens; raise the ceiling&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join(b.text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; message.content &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b.type &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is the counterintuitive result worth keeping, and the A/B below is what made me trust it. More reasoning was not more quality. Past the point the task needed, the extra effort padded the output instead of sharpening it, a small case of perfect being the enemy of good (&lt;a href=&quot;https://en.wikipedia.org/wiki/Perfect_is_the_enemy_of_good&quot;&gt;Perfect is the enemy of good&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Move table rendering into code&lt;/h2&gt;
&lt;p&gt;Low effort had one tell: the model occasionally dropped a row break and collapsed a markdown table into one unreadable line. The durable fix is to stop hoping the model samples the row breaks right. Formatting that has to be correct every time belongs in code, so the weekly metrics table is built by a small deterministic renderer and inserted into the brief.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; render_table&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(headers: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], rows: list[list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]]) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Format a markdown table in code, so output never depends on the model&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    getting the row breaks right.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    line &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cells: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;| &quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot; | &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(c) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; c &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cells) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot; |&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    sep &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;| &quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot; | &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;---&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; headers) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot; |&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join([line(headers), sep, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(line(r) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; r &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; rows)])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Generate a brief through the faster path&lt;/h2&gt;
&lt;p&gt;Now wire it together. The model writes the prose at low effort; the code owns the table and assembles the saved brief. The model never has to render the metrics, so a bad sample can’t corrupt them.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.brief &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; generate_brief, brief_text, render_table&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; daily_brief&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(day: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    snapshot &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.snapshot_for(day)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    prose &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; brief_text(generate_brief(snapshot))          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# low effort by default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    table &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; render_table(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Metric&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Today&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;7-day avg&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Resting HR&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, snapshot[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;resting_hr&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], snapshot[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;resting_hr_7d&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;HRV (ms)&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, snapshot[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;hrv_ms&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], snapshot[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;hrv_ms_7d&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Sleep (h)&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, snapshot[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;sleep_hours&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], snapshot[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;sleep_hours_7d&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    brief &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;prose.strip()&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n\n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;table&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    db.save_brief(day, brief)                              &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# repair/normalize at save&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; brief&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The coach prose now arrives in roughly 80 to 95 seconds, and the metrics table renders clean no matter how the model gets sampled.&lt;/p&gt;
&lt;h2&gt;Verify with a blind A/B&lt;/h2&gt;
&lt;p&gt;The whole change rests on one claim: low effort is at least as good as the old default. Faith in a setting is not evidence, so I scored it. A separate judge call, the same model in a fresh context, sees only the brief text and never learns which effort produced it. Running the same snapshots at both efforts and counting wins is enough to confirm or kill the change. Two things keep the comparison honest: the A/B drops any sample where either arm stopped on &lt;code&gt;max_tokens&lt;/code&gt; instead of &lt;code&gt;end_turn&lt;/code&gt;, so a truncated high-effort brief can’t lose on length alone; and because the judge shares the writer’s model, this is a directional read rather than an oracle, which is why the Next section adds a second judge model.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; anthropic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;client &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; anthropic.Anthropic()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;SCORE_SCHEMA &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;additionalProperties&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: False,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;properties&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # The structured-outputs schema subset has no minimum/maximum, so bound&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # each score with an enum instead. That guarantees a 1-5 integer.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;specificity&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;enum&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;voice&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;enum&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;no_repetition&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;enum&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;no_filler&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;enum&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;required&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;specificity&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;voice&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;no_repetition&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;no_filler&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;JUDGE_SYSTEM &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;Score the brief 1-5 on specificity, voice, no_repetition, no_filler.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; score_brief&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(brief: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Blind: the judge sees only the text, not the effort that made it.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    resp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; client.messages.create(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        model&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;claude-opus-4-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        max_tokens&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        output_config&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;effort&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;low&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;format&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;json_schema&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;schema&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: SCORE_SCHEMA}},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        system&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;JUDGE_SYSTEM,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        messages&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: brief}],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Filter to text blocks: adaptive thinking can prepend a thinking block,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # so content[0] isn&apos;t guaranteed to be the JSON.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join(b.text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.content &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b.type &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; json.loads(text)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; ab_compare&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(snapshots: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], efforts&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;low&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;high&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)) -&gt; dict[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    wins &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {e: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; e &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; efforts}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; snap &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; snapshots:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # Generate every arm first so truncated samples can be dropped before&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # scoring. A high-effort arm that hit max_tokens during thinking would&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # otherwise be scored as a short or empty brief and lose on length, which&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # measures truncation, not quality.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        messages &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {e: generate_brief(snap, effort&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;e) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; e &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; efforts}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(m.stop_reason &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;end_turn&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; m &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; messages.values()):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            continue&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # control for truncation: skip the whole snapshot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        scored &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {e: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;sum&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(score_brief(brief_text(messages[e])).values()) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; e &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; efforts}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        best &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; max&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(scored.values())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        leaders &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [e &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; e, s &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; scored.items() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; s &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; best]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # Ties break toward the earliest effort in the tuple (here &quot;low&quot;): equal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # quality at lower cost is the win I want, and it&apos;s the conservative call&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # against the shipping bar below.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        winner &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; min&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(leaders, key&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;list&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(efforts).index)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        wins[winner] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; wins&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# wins[&quot;low&quot;] &gt;= wins[&quot;high&quot;] is the bar the change had to clear before shipping.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The blind judge scored the low-effort briefs against the old default and the low ones won on every measure: specificity, voice, no repetition, no filler. The only way I’d ever have learned that is by scoring the output instead of assuming more compute is safer.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;A wider A/B across more days and a second judge model to lock in the quality read, and an optional pass that assembles the brief’s data in code to trim the last few seconds of tool round-trips.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Program_optimization&quot;&gt;Program optimization (Knuth on premature optimization)&lt;/a&gt; — profile before you optimize; small efficiencies aren’t where the time is.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt; — start simple, add complexity only when it measurably helps.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Perfect_is_the_enemy_of_good&quot;&gt;Perfect is the enemy of good&lt;/a&gt; — more effort past “good enough” can make the result worse.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://platform.claude.com/docs/en/build-with-claude/effort&quot;&gt;Claude: the effort parameter&lt;/a&gt; — &lt;code&gt;output_config.effort&lt;/code&gt; controls thinking depth and token spend; default is high, lower means terser, cheaper output.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat: brief ~2.5-3x faster via reasoning-effort lever + deterministic tables (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/439e2b30993fe510adc6e5d074430688c4c23829&quot;&gt;439e2b3&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;docs: gated design for brief fan-out (map-reduce) + top-grade CLI output (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/6041c49f0689ac34b396c941fcf16d7826721477&quot;&gt;6041c49&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>A clone is a cache: why /devlog missed a release that was already live</title><link>https://natejswenson.com/devlog/devlog/v0.3.1/</link><guid isPermaLink="true">https://natejswenson.com/devlog/devlog/v0.3.1/</guid><description>/devlog discovered releases by listing local git tags and never fetching, so it skipped a release CI had already cut on the remote. The fix is a refresh-before-read discovery step that fetches first and degrades to local tags when it can&apos;t.</description><pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/devlog&lt;/code&gt; finds new releases by listing your git tags, then writes an entry for any tag that doesn’t have one yet. v0.3.1 fixes a discovery bug: it only ever listed local tags, with no fetch. I hit it for real. A project shipped a release, CI cut the tag on the remote, and &lt;code&gt;/devlog&lt;/code&gt; reported “no new release” because my local checkout was a few tags behind. The fix is a best-effort &lt;code&gt;git fetch --tags&lt;/code&gt; before reading tags, and if the fetch fails because you’re offline or there’s no remote, it logs a note and continues with whatever local tags exist instead of aborting the run.&lt;/p&gt;
&lt;p&gt;It’s a small patch, but it’s a clean example of a mistake that’s easy to make far beyond git: trusting a local copy of state that the world actually owns. Here is the full discovery path, from the refresh through finding undocumented releases to a test that proves a remote-only tag gets seen.&lt;/p&gt;
&lt;h2&gt;A local clone is a cache, not the source of truth&lt;/h2&gt;
&lt;p&gt;The bug was reading the wrong copy of the truth. In a distributed version control system, your clone is a snapshot of the remote from whenever you last synced (&lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes&quot;&gt;Pro Git: Working with Remotes&lt;/a&gt;). The skill never ran a fetch at all, so it only ever saw the tags that were present at clone or last-pull time. Git does auto-follow annotated tags that point at objects a normal fetch already downloaded, but that still leaves a gap: a release tag sitting on a commit you didn’t otherwise fetch won’t arrive unless you ask for tags explicitly with &lt;code&gt;--tags&lt;/code&gt;. So when releases are born on the server, the authoritative list of “what has shipped” lives on the server, and the local tag list is just a cache that’s correct until it isn’t.&lt;/p&gt;
&lt;p&gt;The skill had quietly treated that cache as the single source of truth, and the two disagreed exactly when it mattered, the moment a new release existed remotely but not locally (&lt;a href=&quot;https://en.wikipedia.org/wiki/Single_source_of_truth&quot;&gt;Single source of truth&lt;/a&gt;). The real fix isn’t “add a fetch,” it’s “refresh the cache before you trust it.” Any time a program reads local state to decide something the world owns, whether that’s tags, a config file, or a cached API response, the question to ask is whether that copy can be stale and what reading a stale copy would do. Here it silently skipped a release, which is the worst kind of wrong because nothing errored.&lt;/p&gt;
&lt;h2&gt;Refresh before you read&lt;/h2&gt;
&lt;p&gt;The build is a discovery function that refreshes the cache, then reads it. The fetch comes first so the local tag list reflects what the server knows; the read happens afterward against that freshened list. The order is the whole point.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# devlog/discover.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Refresh remote tags, then read the local list. Best-effort: if there&apos;s no&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# remote or you&apos;re offline, warn and fall back to whatever local tags exist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# rather than aborting the run.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;discover_tags&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; repo&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$1&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -C&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$repo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; remote&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; get-url&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; origin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; 2&gt;&amp;#x26;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -C&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$repo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --tags&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --quiet&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; origin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; 2&gt;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;warn: fetch failed for &lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$repo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;; using local tags (may be stale)&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&amp;#x26;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;warn: no &apos;origin&apos; remote for &lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$repo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;; using local tags only&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&amp;#x26;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # Now authoritative as of the fetch above (or honestly stale, and we said so).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -C&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$repo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --list&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --sort=-creatordate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fetch is the whole fix. The bug was that discovery never fetched at all, so &lt;code&gt;--tags&lt;/code&gt; is what makes a remote-only release tag arrive before we read the list. Note the fetch is read-only: it only adds tags, it never deletes anything local, which is what a “find what shipped” step should do. If you also want to notice tags CI deleted on the remote, that’s an opt-in, not the default: adding &lt;code&gt;--prune --prune-tags&lt;/code&gt; will drop local tags no longer on the remote, but it will also delete purely local tags you created and never pushed, so reach for it only when you actually want that. Sorting by &lt;code&gt;creatordate&lt;/code&gt; gives newest-first, which is the order the entry writer wants.&lt;/p&gt;
&lt;h2&gt;Discover what hasn’t been written yet&lt;/h2&gt;
&lt;p&gt;Refreshed tags are only half the job. The skill writes an entry per release, so discovery has to subtract the tags that already have a file from the tags that exist. That diff is what produces the actual work list, and it’s the call site that consumes &lt;code&gt;discover_tags&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# devlog/discover.sh (continued)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Print every tag that does not yet have a devlog entry on disk.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;find_undocumented_releases&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; repo&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$1&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; content_dir&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$2&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # Versions we&apos;ve already written, derived from entry filenames (v0.3.1.md -&gt; v0.3.1).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # mkdir -p so the first-ever run (no content dir yet) doesn&apos;t make find error out.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  mkdir&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$content_dir&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; documented&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  documented&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;$(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$content_dir&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot; -name &apos;v*.md&apos; -exec basename {} .md &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; sort&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -u)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  discover_tags&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$repo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; while&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; read&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; tag&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -qxF&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &amp;#x3C;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$documented&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   # a release with no entry yet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Call site: anything printed here is a release /devlog still needs to write up.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;find_undocumented_releases&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$HOME&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/code/myproject&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;./content/devlog/myproject&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-z&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$new&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ]; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;no new release&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;writing entries for:&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$new&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before the fix, &lt;code&gt;discover_tags&lt;/code&gt; was just &lt;code&gt;git tag --list&lt;/code&gt;, so the moment the remote was ahead the diff came back empty and the skill printed “no new release” with total confidence. With the refresh in front, the same run now sees the remote-only tag and queues it. On an offline run you get the warning on stderr and the best-available answer on stdout, for example:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;warn: fetch failed for /home/me/code/myproject; using local tags (may be stale)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;writing entries for:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;v0.3.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Fail soft when the refresh can’t happen&lt;/h2&gt;
&lt;p&gt;The fetch is best-effort on purpose. A devlog tool that refused to run without a network would be worse than one that occasionally works from a slightly stale cache, so a failed fetch logs a note and falls back to local tags rather than aborting. That’s the same principle behind graceful degradation: keep partial, useful function during a failure instead of shutting down, and make the reduced mode visible rather than silent. The cited guidance frames this around overload and load-shedding, but the general principle carries straight over to a dependency or network failure like a missing remote (&lt;a href=&quot;https://docs.cloud.google.com/architecture/framework/reliability/graceful-degradation&quot;&gt;Google Cloud: Design for graceful degradation&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The visibility is what keeps “fail soft” from becoming “fail silent.” The warning goes to stderr while the tag list goes to stdout, so a human sees the degraded-mode note and a downstream pipe still gets clean data. The only failure this design refuses to absorb is the original one, treating a stale cache as authoritative without anyone knowing.&lt;/p&gt;
&lt;h2&gt;Verify it&lt;/h2&gt;
&lt;p&gt;The bug was invisible because it depended on local and remote disagreeing, a state that’s annoying to reach by hand. A test can manufacture it deterministically: stand up a bare repo as the remote, clone it, then create a tag directly on the remote so the clone is genuinely behind. Good discovery must surface that tag; broken discovery returns nothing.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_discover.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -euo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; pipefail&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; devlog/discover.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Set a git identity for this process so `commit` works on a bare CI runner.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; GIT_AUTHOR_NAME&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;t GIT_AUTHOR_EMAIL&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;t@t.t&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; GIT_COMMITTER_NAME&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;t GIT_COMMITTER_EMAIL&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;t@t.t&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;tmp&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;mktemp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; init&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --bare&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tmp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/remote.git&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tmp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/remote.git&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tmp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/clone&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -C&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tmp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/clone&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; commit&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --allow-empty&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; init&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -C&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tmp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/clone&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; push&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; origin&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; HEAD:main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Cut a release on the remote only; the clone never fetched it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;work&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;mktemp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tmp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/remote.git&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$work&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -C&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$work&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; commit&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --allow-empty&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; release&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -C&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$work&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; v0.3.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -C&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$work&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; push&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; origin&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; v0.3.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 1. refresh-before-read finds the remote-only tag.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Note: under `set -e`, `cmd &amp;#x26;&amp;#x26; echo ok` would NOT fail the run on a false left&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# side, so a regression reads green. Each check has to hard-fail explicitly.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; discover_tags&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tmp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/clone&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -qxF&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; v0.3.1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;FAIL: remote-only tag was not discovered&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&amp;#x26;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exit&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;ok: remote-only tag discovered&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 2. fail-soft: a dead remote degrades to local tags instead of crashing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -C&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tmp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/clone&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; remote&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; set-url&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; origin&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; /nonexistent.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -C&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tmp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/clone&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; v0.0.1&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;            # one local tag to fall back to&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;out&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;discover_tags&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tmp&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/clone&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; 2&gt;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -qxF&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; v0.0.1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &amp;#x3C;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$out&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;FAIL: degraded mode did not return local tags&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&amp;#x26;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exit&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;ok: degraded to local tags&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first assertion fails against the old &lt;code&gt;git tag --list&lt;/code&gt; discovery, which is exactly the regression that shipped. The second fails if a missing or broken remote ever starts aborting the run instead of warning and continuing.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;Next I want to tighten the entry-filename convention so date-named and version-named entries can’t drift apart, and lean on the shared release CI for the rest of the skills.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes&quot;&gt;Pro Git: Working with Remotes&lt;/a&gt; — a clone syncs from the remote; you fetch to update it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-fetch&quot;&gt;git fetch documentation&lt;/a&gt; — &lt;code&gt;--tags&lt;/code&gt; fetches all tags from the remote; it only adds, it never deletes local tags.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Single_source_of_truth&quot;&gt;Single source of truth&lt;/a&gt; — read the authoritative copy, not a stale local cache of it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.cloud.google.com/architecture/framework/reliability/graceful-degradation&quot;&gt;Google Cloud: Design for graceful degradation&lt;/a&gt; — keep partial, useful function during a failure rather than shutting down.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;fix(devlog): fetch tags before release discovery (0.3.1) (#7) (&lt;a href=&quot;https://github.com/natejswenson/claude-skills/commit/859c58b2252bea4021305538ec37f4f5040f1a0e&quot;&gt;859c58b&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;ci: reusable release + per-skill callers, shared skill scorer, devlog tests (#5) (&lt;a href=&quot;https://github.com/natejswenson/claude-skills/commit/c3870d67ccb7b63b2182ec94635e32d55085da01&quot;&gt;c3870d6&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Logging releases, not days: idempotency, voice-as-config, and untrusted tags</title><link>https://natejswenson.com/devlog/devlog/v0.3.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/devlog/v0.3.0/</guid><description>devlog switched from a daily commit log to one idempotent entry per release tag, written from a configurable voice profile. Here is the full build: per-project config, validating every tag as untrusted input, making the tag the idempotency key, and resolving voice from an external profile.</description><pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;devlog used to summarize each day’s commits. v0.3.0 makes it summarize each release: when I tag a version, it writes one entry for that release and pushes it, and re-running does nothing until the next tag. Entries now come from a configurable voice profile instead of a generic standup tone, so the log sounds like me. In a monorepo each project sets its own tag prefix so several skills track releases independently, only real version tags become entries, and tag names are validated as untrusted input before they ever touch a shell.&lt;/p&gt;
&lt;p&gt;This post is itself one of those entries, which makes it a decent test of whether the format holds up. The feature is “one entry per release,” but the part worth writing about is the three decisions underneath it: anchor the work on release tags so re-runs are idempotent, treat the voice as external configuration instead of code, and treat the tag names git hands you as untrusted input. Here is the end-to-end build.&lt;/p&gt;
&lt;h2&gt;Setup: per-project config and a voice path&lt;/h2&gt;
&lt;p&gt;Everything project-specific lives in config so the generator itself stays generic. Every project lives in one repo (a monorepo); each declares its &lt;code&gt;tagPrefix&lt;/code&gt; and a &lt;code&gt;pathFilter&lt;/code&gt;, so the generator can track several release streams independently and scope each one’s commits to its own directory. The run declares a &lt;code&gt;voicePath&lt;/code&gt;, an ordered list of places to look for the voice profile. The first file that exists wins, which is what lets the same skill run for anyone: the behavior is shared, the personality is supplied.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// devlog.config.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // Every project lives in this one repo (a monorepo), so every git command runs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // against the same working tree; a per-project pathFilter scopes each one&apos;s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // commits, reusing the pathFilter seam added in v0.2.0.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  repo: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;claude-skills&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  projects: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    devlog: { tagPrefix: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,        entriesDir: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content/devlog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, pathFilter: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;skills/devlog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    resume: { tagPrefix: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;resume-v&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, entriesDir: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content/resume&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, pathFilter: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;skills/resume&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // Voice profile search path, checked in order. First hit wins; if no listed&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // file exists, resolveVoice falls back to the bundled default, so the tool&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // runs even with no user config.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  voicePath: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;./.devlog/voice.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;~/.config/ghostwriter/voice.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;tagPrefix&lt;/code&gt; is the seam that makes the rest of the tool release-anchored rather than day-anchored. Instead of asking “what did I touch today,” every step from here keys off the set of version tags that match that prefix.&lt;/p&gt;
&lt;h2&gt;Enumerate releases and validate every tag as untrusted input&lt;/h2&gt;
&lt;p&gt;A tag name is just text someone wrote, and this tool puts that text next to shell commands and filesystem paths. “It came from my repo” is not the same as “it’s safe to interpolate into a command,” and the gap between those two is where command injection lives. Git ref names are more permissive than you would guess; a name like &lt;code&gt;v1.0.0$(id)&lt;/code&gt; is a perfectly legal tag that would be catastrophic if it reached a shell unquoted.&lt;/p&gt;
&lt;p&gt;So before a tag is used for anything, it is checked against what a tag is &lt;em&gt;allowed&lt;/em&gt; to be, and everything else is rejected. That is the allowlist principle: define what’s valid and reject the rest, which OWASP recommends precisely because the alternative, blocking known-bad patterns, is easy to evade (&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html&quot;&gt;OWASP: Input Validation&lt;/a&gt;; &lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_Cheat_Sheet.html&quot;&gt;OWASP: Injection Prevention&lt;/a&gt;).&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -euo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; pipefail&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# One argument: the project key. The tag prefix and output dir come from&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# devlog.config.js, so a monorepo tracks each project&apos;s releases on its own.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;PROJECT&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;${1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:?&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;usage&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; gen-entries&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;projectKey&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Capture the config in a command substitution (not a process substitution): the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# explicit `|| exit 1` stops the run on a non-zero node exit, so an unknown&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# project stops the run instead of silently leaving PREFIX/ENTRIES_DIR empty.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;CONFIG&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;$(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -e &apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  const { projects } = require(&quot;./devlog.config&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  const p = projects[process.argv[1]];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  if (!p) { console.error(&quot;unknown project: &quot; + process.argv[1]); process.exit(1); }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  process.stdout.write(`${p.tagPrefix}\t${p.entriesDir}`);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&apos; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$PROJECT&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; exit&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;IFS&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;$&apos;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\t&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; read&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; PREFIX&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; ENTRIES_DIR&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &amp;#x3C;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$CONFIG&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[[ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$PREFIX&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$ENTRIES_DIR&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ]] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;bad config for &lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$PROJECT&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&amp;#x26;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exit&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# A tag is attacker-controllable input. First reject anything not shell/path-safe&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# (the security boundary); then keep only tags shaped like a release version, so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# &quot;vanity&quot;, &quot;version-2&quot;, and &quot;v1..0&quot; never become entries.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;is_safe_tag&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;()    { [[ &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$1&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =~&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ^[A-Za-z0-9][A-Za-z0-9._-]&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$ ]]; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;is_release_tag&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() { [[ &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;${1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$PREFIX&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =~&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ^[0-9]+&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\.&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[0-9]+&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\.&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[0-9]&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;-[A-Za-z0-9.]+&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$ ]]; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Ensure the output dir exists so the atomic write&apos;s `&gt; &quot;$out.tmp&quot;` redirection&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# can&apos;t fail on a fresh checkout where content/devlog isn&apos;t there yet.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$ENTRIES_DIR&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --list&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;PREFIX&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}*&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; while&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; IFS&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; read&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; tag&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; is_safe_tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    printf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &apos;skip: invalid tag name %q\n&apos;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&amp;#x26;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    continue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; is_release_tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    printf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &apos;skip: %s is not a release version\n&apos;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&amp;#x26;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    continue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # tag is now shell-safe and a real release; safe to use below.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The shell-safety check runs on the way &lt;em&gt;in&lt;/em&gt;, before the tag is interpolated anywhere: a name with a &lt;code&gt;$&lt;/code&gt;, a backtick, a slash sequence, or a leading dash never makes it past the &lt;code&gt;continue&lt;/code&gt;. The release-shape check is a separate filter; it answers “is this actually a version I should write up,” and it is the reason a stray branch-like or marketing tag is ignored instead of generating a bogus entry.&lt;/p&gt;
&lt;h2&gt;Idempotency: the tag is the key&lt;/h2&gt;
&lt;p&gt;Anchoring an entry to a version tag makes the tool safe to re-run. A tag either has an entry file or it doesn’t, so running the generator twice produces the same result as running it once. That is idempotency, the property that applying an operation repeatedly has the same effect as applying it once (MDN frames this for HTTP methods, but the property is general: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Idempotent&quot;&gt;MDN: Idempotent&lt;/a&gt;). It matters because it removes a class of bookkeeping: the entry filename derived from the tag &lt;em&gt;is&lt;/em&gt; the idempotency key, so the tool never has to track which tags it already processed.&lt;/p&gt;
&lt;p&gt;Save the validation script above as &lt;code&gt;gen-entries&lt;/code&gt; and make it executable (&lt;code&gt;chmod +x gen-entries&lt;/code&gt;); the block below replaces that final placeholder comment inside the loop.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ...inside the validated loop from the previous block:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  out&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;ENTRIES_DIR&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}/${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}.md&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # Idempotent: a tag that already has an entry is a no-op. The tag is the key.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [[ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$out&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ]]; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    printf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &apos;skip: %s already has an entry\n&apos;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    continue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # Write atomically: generate to a temp file and publish only on success, so a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # failed run never leaves a half-written entry that the check above would then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # treat as &quot;done&quot; forever.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; node&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; generate.js&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$PROJECT&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$out&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;.tmp&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    rm&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$out&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;.tmp&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    printf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &apos;fail: %s could not be generated\n&apos;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&amp;#x26;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    continue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  mv&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$out&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;.tmp&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$out&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # Commit and push the new entry. The file write above is the idempotent part;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # the push is best-effort. If it fails after the commit lands, the entry is&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # committed locally and the next run (seeing the file) won&apos;t retry the push, so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # re-push by hand.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$out&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; commit&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;devlog: ${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; push&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; printf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &apos;warn: %s committed locally but push failed; re-push manually\n&apos;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&amp;#x26;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  printf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &apos;wrote: %s\n&apos;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$out&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the existence of &lt;code&gt;&amp;quot;$out&amp;quot;&lt;/code&gt; is the only thing that decides whether work happens, there is no separate “have I processed this tag” record to keep in sync with the filesystem.&lt;/p&gt;
&lt;h2&gt;Resolve the voice profile and write the entry&lt;/h2&gt;
&lt;p&gt;The voice that makes the log sound like me does not live in the tool. It is a profile resolved from the configured &lt;code&gt;voicePath&lt;/code&gt;, falling back through the search list to a bundled default. The resolver does one job: walk the candidates, return the first that exists, otherwise the default. Keeping voice as external config rather than hard-coding it is the same instinct as the rest of the config; the tool stays generic and the user-specific parts are values it reads, not assumptions it bakes in.&lt;/p&gt;
&lt;p&gt;The profile is shared with the LinkedIn ghostwriter, so it carries both writing rules and feed-tuning knobs. A minimal &lt;code&gt;voice.default.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;tone&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;plain&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;concrete&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;first-person&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;neverDo&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;em dashes in prose&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;rhetorical questions&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;hype words&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;reach&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;hashtags&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;hook&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;question&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;engagement&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;cta&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ask for replies&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// voice.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fs &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;node:fs&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;node:os&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; path &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;node:path&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; BUNDLED_DEFAULT &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;./voice.default.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; expand&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (p) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  p.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;startsWith&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;~&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; path.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(os.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;homedir&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(), p.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// Fallback chain: first existing file in voicePath wins, else bundled default.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; resolveVoice&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(voicePath) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; candidate &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;of&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; voicePath) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; file &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; expand&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(candidate);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (fs.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;existsSync&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(file)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; JSON.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(fs.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;readFileSync&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(file, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; BUNDLED_DEFAULT;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// A dev log is not a feed: keep tone and never-do rules, drop the reach tuning.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; devlogVoice&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(profile) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { reach, engagement, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;rest } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; profile;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; rest;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { resolveVoice, devlogVoice };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The generator takes the same project key the bash loop was invoked with, scopes the commit range to that project’s path, wires the resolved voice to it, and renders the entry. The voice is passed in as data, so swapping profiles never touches this code.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// generate.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { resolveVoice, devlogVoice } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;./voice&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { commitsForRelease, renderPost } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;./render&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { voicePath, projects, repo } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;./devlog.config&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; generateEntry&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(tag, projectKey &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;devlog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; project &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; projects[projectKey];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;project) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;throw&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`unknown project: ${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;projectKey&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; voice &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; devlogVoice&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;resolveVoice&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(voicePath));   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// external config, not code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; commits &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; commitsForRelease&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(tag, project); &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// prevTag..tag, scoped to project.pathFilter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; renderPost&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;({ tag, repo, commits, voice });      &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// -&gt; markdown&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// CLI: `node generate.js &amp;#x3C;tag&gt; &amp;#x3C;projectKey&gt;` prints the entry to stdout (the bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// loop redirects it). A thrown error exits non-zero, so a failed generation is a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// non-zero exit rather than empty stdout, and the caller never publishes it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (require.main &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; module&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  generateEntry&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(process.argv[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], process.argv[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;((md) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; process.stdout.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;write&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(md))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;((err) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { console.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(err); process.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exit&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;); });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { generateEntry };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The commit range itself comes from &lt;code&gt;render.js&lt;/code&gt;, which scopes each entry to exactly one release and one project: the commits in &lt;code&gt;prevTag..tag&lt;/code&gt; that touched the project’s &lt;code&gt;pathFilter&lt;/code&gt;, not everything since the tag. &lt;code&gt;prevTag&lt;/code&gt; is the previous tag with the same prefix (so in the monorepo another project’s release is never mistaken for this one’s predecessor), and it is empty for the very first release, which makes the range the full history up to the tag.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// render.js (excerpt)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { execFile } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;node:child_process&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { promisify } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;node:util&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; run &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; promisify&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(execFile);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// Commits belonging to THIS release: the range prevTag..tag, scoped to this&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// project&apos;s path. Find the previous tag with the SAME prefix (so another&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// project&apos;s tag in the monorepo isn&apos;t mistaken for this one&apos;s predecessor); if&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// there isn&apos;t one (first release), fall back to the full history. Every git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// command runs in the one repo&apos;s working tree.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; commitsForRelease&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(tag, { tagPrefix, pathFilter }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { stdout: prev } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;describe&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;--abbrev=0&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;--tags&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;--match&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;tagPrefix&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}*`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}^`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  ]).&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ({ stdout: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; }));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; range &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; prev.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;trim&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; `${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;prev&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;trim&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}..${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tag;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { stdout } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;log&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;--format=%s&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, range, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;--&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, pathFilter]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; stdout.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(Boolean);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// renderPost (omitted) turns { tag, repo, commits, voice } into the markdown body.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { commitsForRelease, renderPost };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;renderPost&lt;/code&gt; is deliberately out of scope here: it is the one piece you supply. Its contract is small, it takes &lt;code&gt;{ tag, repo, commits, voice }&lt;/code&gt; and returns the entry’s markdown, so how you turn commits and a voice profile into prose is yours to decide.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;devlogVoice&lt;/code&gt; strips the LinkedIn reach and engagement tuning before the profile reaches the renderer, because a dev log is not a feed; it keeps the tone and the never-do rules and ignores the parts that only make sense for a social timeline.&lt;/p&gt;
&lt;h2&gt;Verify: re-runs are no-ops, bad tags are skipped&lt;/h2&gt;
&lt;p&gt;The two decisions worth defending are the ones you can demonstrate from the shell: running twice changes nothing the second time, and a malformed tag never reaches generation or the filesystem.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 1. First run writes, commits, and pushes an entry for the new release tag.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; ./gen-entries&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; devlog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;wrote:&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; content/devlog/v0.3.0.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 2. Re-running is a no-op: the tag already has an entry (idempotent).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; ./gen-entries&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; devlog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;skip:&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; v0.3.0&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; already&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; has&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; an&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; entry&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 3. A legal git tag that fails the allowlist is skipped before it can run.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &apos;v9.9.9$(id)&apos;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;                  # a valid git ref, but hostile as a shell string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; ./gen-entries&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; devlog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;skip:&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; invalid&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; tag&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; v9.9.9&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\$\(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\)&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # printf %q escaping; rejected on the way in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second run proves the tag is the idempotency key: no duplicate file, no second push, no edited entry. The third proves the allowlist holds at the boundary. Every use site is already quoted (&lt;code&gt;&amp;quot;$tag&amp;quot;&lt;/code&gt;, &lt;code&gt;&amp;quot;${ENTRIES_DIR}/${tag}.md&amp;quot;&lt;/code&gt;, &lt;code&gt;-m &amp;quot;devlog: ${tag}&amp;quot;&lt;/code&gt;) or passed through &lt;code&gt;execFile&lt;/code&gt; with no shell, and parameter expansion inside double quotes doesn’t re-trigger command substitution, so &lt;code&gt;$(id)&lt;/code&gt; is inert even without the check. &lt;code&gt;is_safe_tag&lt;/code&gt; is defense-in-depth: even though every use site is quoted, it rejects hostile names on the way in. If either property regresses, this exact transcript stops matching, which makes it a cheap thing to keep as a smoke test.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;I want to see how these voice-written entries read across the next few releases, then tighten the rendering template from what those show. That is, more or less, exactly what is happening now.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Idempotent&quot;&gt;MDN: Idempotent&lt;/a&gt; — applying an operation repeatedly has the same effect as applying it once (MDN frames the term for HTTP methods; the property generalizes).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html&quot;&gt;OWASP: Input Validation Cheat Sheet&lt;/a&gt; — allowlist what’s valid; denylists are evadable.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_Cheat_Sheet.html&quot;&gt;OWASP: Injection Prevention Cheat Sheet&lt;/a&gt; — don’t trust external input near an interpreter or shell.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat(devlog): release-focused entries, written in your voice (0.3.0) (#4) (&lt;a href=&quot;https://github.com/natejswenson/claude-skills/commit/904ceb62809ef3fafb933b7ddfaebb4485f90a24&quot;&gt;904ceb6&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>When the agent can read and write everything, the server stops running the model</title><link>https://natejswenson.com/devlog/local-fitness/v0.5.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/local-fitness/v0.5.0/</guid><description>Once the agent can both read and write local-fitness over MCP, the synthesis no longer needs to live inside the server. I moved the daily brief out to a client agent that drives the same MCP tools, leaving the server as pure data and deterministic compute. Here is the full build, plus the siege finding that a &apos;read-only&apos; SQL guard wasn&apos;t read-only.</description><pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;This was the big one: I inverted the architecture of local-fitness. It was already a Claude-powered app, but the daily brief and the chat both ran inside the web server. Now the server runs no LLM inference at all. It serves my Garmin data and the deterministic compute, recovery baselines, the CTL/ATL/TSB training-load model, training-plan grading, over REST and MCP, and every act of synthesis, the brief and the coaching, moves out to a client agent I talk to from Claude Desktop, Code, or Mobile. I shipped it in phases behind gates, then ran an adversarial quality-gate and siege against the running container, which caught a real Fatal before it could matter.&lt;/p&gt;
&lt;p&gt;v0.3.0 set this up: it put the data and the compute behind MCP, with a write surface added alongside the read surface. The part worth writing about is what that unlocked. Once the agent can read the snapshot &lt;em&gt;and&lt;/em&gt; write the brief over the same tools, the synthesis loop has no reason to live in the server. Here is the end-to-end build of moving it out, and the security finding the siege surfaced on the way.&lt;/p&gt;
&lt;h2&gt;The cut line: deterministic compute as code, synthesis at the edge&lt;/h2&gt;
&lt;p&gt;The decision that made the rearchitecture tractable was where to cut. Deterministic compute stays as code on the server. Probabilistic synthesis moves to the agent. The training-load math and the plan grading have one correct answer and must be testable, repeatable, and fast, so they belong in code. The brief and the coaching are judgment and language, which is what the model is for, so they belong in the agent.&lt;/p&gt;
&lt;p&gt;That line maps onto Anthropic’s framing of workflows versus agents: keep the predictable parts in predefined code paths, and reserve the model for the work that genuinely needs model-driven flexibility (&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt;). The payoff is that each side gets simpler. The server stops holding an API key and running inference loops, and becomes a clean data-and-compute surface. The agent stops being trapped inside one app’s brief loop and can reason over the same MCP tools from anywhere I talk to Claude. The web UI drops to what it’s actually good at, a fast visual glance.&lt;/p&gt;
&lt;h2&gt;Setup: the MCP tool surface the agent drives&lt;/h2&gt;
&lt;p&gt;The agent talks to the same MCP server every other client uses. The official MCP Python SDK gives you a client session over stdio: you launch the server as a subprocess, initialize, and discover its tools at runtime (&lt;a href=&quot;https://github.com/modelcontextprotocol/python-sdk&quot;&gt;MCP Python SDK&lt;/a&gt;). Because v0.3.0 already exposed both a read surface (&lt;code&gt;daily_snapshot&lt;/code&gt;, &lt;code&gt;run_sql&lt;/code&gt;, the training-load model) and a write surface (&lt;code&gt;save_brief&lt;/code&gt;, &lt;code&gt;log_observation&lt;/code&gt;), the agent has everything it needs to gather context and to write the result back.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;python&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; venv&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; .venv&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;source&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; .venv/bin/activate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;pip&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; anthropic&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;mcp[cli]&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;pip&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; pytest&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; anyio&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # anyio backs the @pytest.mark.anyio async test below&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open one session and convert whatever the server advertises into the shape the Messages API expects. Discovering tools at runtime means a new server tool shows up to the agent with no client change.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# agent/session.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; contextlib&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ClientSession, StdioServerParameters&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.client.stdio &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; stdio_client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;FITNESS_SERVER &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; StdioServerParameters(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    command&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;python&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, args&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;-m&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness.transports&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;],  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the v0.3.0 stdio entry point&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@contextlib.asynccontextmanager&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; fitness_session&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Open an MCP client session against the local fitness server.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; stdio_client(FITNESS_SERVER) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (read, write):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ClientSession(read, write) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; session:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            await&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; session.initialize()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            yield&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; session  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# session.list_tools() / session.call_tool(...) live here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; to_anthropic_tools&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(mcp_tools) -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;MCP tool definitions -&gt; Messages API tool schema.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: t.name,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;description&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: t.description &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;or&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;input_schema&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: t.inputSchema,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp_tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build: the agent loop that writes the brief&lt;/h2&gt;
&lt;p&gt;This is the loop that used to be server-side synthesis. The model reads the day’s data through the read tools, decides what matters, writes the brief, and saves it with the write tool. The harness is a plain agentic loop: call the model, execute any tool calls against the MCP session, feed the results back, repeat until the model stops asking for tools (&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt;). The model is &lt;code&gt;claude-opus-4-8&lt;/code&gt; with adaptive thinking, which lets it decide how much to reason per step.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# agent/brief.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; anthropic &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; AsyncAnthropic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; agent.session &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; to_anthropic_tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;client &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; AsyncAnthropic()  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# reads ANTHROPIC_API_KEY; the key lives with the agent, not the server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;COACH_SYSTEM &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;You are my training coach. Read today&apos;s data through the fitness tools, &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;write a short daily brief, and save it with the save_brief tool. &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;Use only values the tools return; never invent a number.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; run_agent&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(session, prompt: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    listed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; session.list_tools()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    tools &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; to_anthropic_tools(listed.tools)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    messages &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: prompt}]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    while&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; True:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        resp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; client.messages.create(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            model&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;claude-opus-4-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            max_tokens&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;4096&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            thinking&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;adaptive&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            system&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;COACH_SYSTEM,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            tools&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;tools,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            messages&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;messages,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        messages.append({&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;assistant&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: resp.content})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.stop_reason &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;tool_use&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; next&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;((b.text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.content &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b.type &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        results &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; block &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.content:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; block.type &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;tool_use&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                continue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            out &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; session.call_tool(block.name, block.input)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the MCP round-trip&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            results.append({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;                &quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;tool_result&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;                &quot;tool_use_id&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: block.id,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;                &quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: _render(out)}],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;                &quot;is_error&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: out.isError,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        messages.append({&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: results})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; _render&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(result) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join(c.text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; c &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; result.content &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; c.type &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The whole brief is now tool calls and a model. The server never sees the prompt, never runs the loop, and never holds the key.&lt;/p&gt;
&lt;h2&gt;Use it: generate a brief end to end&lt;/h2&gt;
&lt;p&gt;Wiring the session to the loop is the entire entry point. The agent connects, reads the latest snapshot (and &lt;code&gt;run_sql&lt;/code&gt; for the week if it wants the trend), composes the brief, and calls &lt;code&gt;save_brief&lt;/code&gt; so the same row the web UI reads gets written.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# generate.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; asyncio&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; agent.session &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness_session&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; agent.brief &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; run_agent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness_session() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; session:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        summary &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; run_agent(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            session,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;Write today&apos;s brief from my latest data and save it.&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;        print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(summary)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __name__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    asyncio.run(main())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A run reads as a tool trace: &lt;code&gt;daily_snapshot&lt;/code&gt; for today, sometimes &lt;code&gt;run_sql&lt;/code&gt; to pull the last seven days of sleep and load, then &lt;code&gt;save_brief&lt;/code&gt; with the composed text. That last write is the point. The web frontend became a viewer of agent-written output, so the brief I generate from Claude Desktop is the brief the UI shows, with no second code path generating it server-side.&lt;/p&gt;
&lt;h2&gt;Verify: the server holds no inference&lt;/h2&gt;
&lt;p&gt;Two things are worth a test. First, that the server actually stopped doing synthesis, which I assert structurally by proving no server module imports the model SDK. Second, that the agent path can both read and write, the precondition that let synthesis leave the server in the first place.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_agent_first.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pytest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; agent.session &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness_session&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@pytest.fixture&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; anyio_backend&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;asyncio&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # anyio needs this fixture to know which backend to run on&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_server_runs_no_inference&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;The server is data + deterministic compute only; it imports no model SDK.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    root &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib.Path(fitness.__file__).parent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; path &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; root.rglob(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;*.py&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; path.read_text()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        assert&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;import anthropic&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; text, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; still runs inference&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        assert&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;from anthropic&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; text, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; still runs inference&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@pytest.mark.anyio&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_agent_can_read_and_write&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;The agent path reaches both surfaces, so the brief can land back over MCP.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness_session() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; session:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        names &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {t.name &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; session.list_tools()).tools}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;daily_snapshot&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; names  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# read path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;save_brief&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; names      &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# write path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first test fails the moment any server module pulls the model back in, which is the regression I most want to catch. The second confirms the agent reaches both surfaces. Beyond the tests, the way you confirm the result is as good is to run the agent and diff the saved brief against what the old in-server loop produced for the same day; in practice it reads better, because the agent can pull the week with &lt;code&gt;run_sql&lt;/code&gt; when the day alone is thin.&lt;/p&gt;
&lt;h2&gt;A “read-only” denylist is not read-only&lt;/h2&gt;
&lt;p&gt;The part I’m most glad I did was siege the live, running container and its MCP surface rather than just the source, and it earned its keep immediately by finding a Fatal. The &lt;code&gt;run_sql&lt;/code&gt; tool enforced “read-only” with a keyword denylist, but it only checked the query’s leading keyword: if the statement started with a benign token, it was waved through. So a query that opens with a &lt;code&gt;WITH&lt;/code&gt; CTE hides the write that follows, and it slipped straight past the guard and committed, demonstrated live: &lt;code&gt;WITH a AS (SELECT 1) DELETE FROM workouts&lt;/code&gt; starts with &lt;code&gt;WITH&lt;/code&gt;, so the denylist never sees the &lt;code&gt;DELETE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is the textbook failure of denylisting, and OWASP names it directly: a denylist tries to enumerate known-bad input and is reliably evadable through casing, whitespace, and phrasing the author didn’t anticipate (&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html&quot;&gt;OWASP: Input Validation&lt;/a&gt;; &lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_Cheat_Sheet.html&quot;&gt;OWASP: Injection Prevention&lt;/a&gt;). The fix was to stop guessing at the string layer and enforce the property where it can actually be guaranteed: open the database connection read-only at the SQLite engine itself, so a write fails regardless of how the query is phrased.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sqlite3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; readonly_connection&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(db_path: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; sqlite3.Connection:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # `mode=ro` via a file: URI makes the engine reject every write,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # no matter how the SQL is phrased (CTEs, casing, leading tokens, all of it).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sqlite3.connect(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;file:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;db_path&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;?mode=ro&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, uri&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;conn &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; readonly_connection(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness.db&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;conn.execute(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;WITH a AS (SELECT 1) DELETE FROM workouts&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# sqlite3.OperationalError: attempt to write a readonly database&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The denylist tried and failed to recognize that query as a write; the read-only connection rejects it at the engine without parsing it at all. I also bounded and offloaded execution after an unbounded query froze the single event-loop thread (an authenticated denial of service), and hardened input validation across the rest of the MCP tools, with 19 new regression tests. The gate converged clean over three rounds. The lesson is about layers: the right place to enforce “this cannot write” is the layer that owns writing, not a filter in front of it, and the bug only showed up because the siege exercised the deployed surface a client actually reaches, not just the code.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;Set the container timezone to local to kill a phantom-data-row at its source, and keep leaning into MCP as the primary surface. The goal is to reach for the coach in Claude more than the UI.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt; — keep predictable work in code paths; reserve the model for what needs it, and the agentic loop pattern that drives tools.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/modelcontextprotocol/python-sdk&quot;&gt;MCP Python SDK&lt;/a&gt; — &lt;code&gt;ClientSession&lt;/code&gt; over stdio, &lt;code&gt;list_tools&lt;/code&gt;, and &lt;code&gt;call_tool&lt;/code&gt; for driving a server from a client.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html&quot;&gt;OWASP: Input Validation Cheat Sheet&lt;/a&gt; — denylists are evadable; prefer allowlists and enforce at the right layer.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_Cheat_Sheet.html&quot;&gt;OWASP: Injection Prevention Cheat Sheet&lt;/a&gt; — don’t decide safety by pattern-matching the input string.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat: agent-first Phase 1 — briefs write gate, save_brief tool, brief prompt (#25) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/590f6bb35418a03d78c17d485d89a02c6cbe722c&quot;&gt;590f6bb&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;feat: agent-first Phase 2 — frontend becomes a viewer of agent-written output (#25) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/1980ea6fba9f740a93f1e5632544e08dd7147ccc&quot;&gt;1980ea6&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;feat: agent-first Phase 3 — retire the server-side Claude loops (#25) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/8a500cee9060560910b2b0e93f281ac4684b112e&quot;&gt;8a500ce&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;fix(security): harden run_sql + MCP tool inputs (quality-gate findings, #25) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/35d52300e38e8eac8d1e00d34815aec4d2e4be2f&quot;&gt;35d5230&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;fix: container build — Debian web-builder for rolldown + pnpm pin (#25) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/28473377314b37db693d4127082d09356c0476b8&quot;&gt;2847337&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Make MCP the front door: one server, a read-only fence, and a localhost that still needs locks</title><link>https://natejswenson.com/devlog/local-fitness/v0.3.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/local-fitness/v0.3.0/</guid><description>I put my self-hosted fitness data behind the Model Context Protocol so I can query and update it straight from an AI client. Here is the full build: defining tools and resources, serving one server over two transports, fencing the brief loop to read-only at the structure level, and the security a localhost MCP server actually needs.</description><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;Until v0.3.0 the useful part of local-fitness lived in the wrong place. My Garmin data sits in SQLite, a Claude agent writes a daily briefing, and all of that ran server-side in a brief loop that no AI client could reach. Opening Claude to ask “how did I sleep this week” did not really work, because the data and the coaching had no front door.&lt;/p&gt;
&lt;p&gt;This release makes MCP that front door. There is a &lt;code&gt;coach&lt;/code&gt; prompt that assembles the daily snapshot and persona in one round-trip, &lt;code&gt;fitness://schema&lt;/code&gt; and &lt;code&gt;fitness://brief/latest&lt;/code&gt; resources, a one-call &lt;code&gt;daily_snapshot&lt;/code&gt; tool, miles formatted server-side, and for the first time a write surface so observations and manual workouts can feed the training-load model. The feature is “talk to my app over MCP.” The part worth writing about is how to expose your own app this way without duplicating your server, without letting the read path mutate data, and without trusting “it’s only on my machine” as a security model. Here is the end-to-end build.&lt;/p&gt;
&lt;h2&gt;Setup: the server and its tools&lt;/h2&gt;
&lt;p&gt;The Model Context Protocol is a standard surface of tools, resources, and prompts that any compatible client can reach (&lt;a href=&quot;https://www.anthropic.com/news/model-context-protocol&quot;&gt;Anthropic: Introducing MCP&lt;/a&gt;). The official Python SDK ships &lt;code&gt;FastMCP&lt;/code&gt;, which turns plain functions into that surface with decorators (&lt;a href=&quot;https://github.com/modelcontextprotocol/python-sdk&quot;&gt;MCP Python SDK&lt;/a&gt;). Start with the dependencies.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;python&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; venv&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; .venv&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;source&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; .venv/bin/activate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;pip&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;mcp[cli]&quot;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; uvicorn&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; starlette&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now define the read surface. A tool performs an action and returns structured data; a resource is addressable, read-only context the client can load by URI. The snapshot tool does the join across your SQLite tables once, and the schema resource lets a client discover the shape before it asks anything.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/server.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.server.fastmcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# your own data layer over SQLite&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# db.snapshot_for(day) returns a row exposing the attributes used below:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;#   day, resting_hr, hrv_ms, sleep_minutes, distance_meters, training_load&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# (a dataclass, sqlite3.Row, or any object with those fields). Substitute&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# your own join over whatever tables hold sleep, HRV, and workout data.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; register_read_tools&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(mcp: FastMCP) -&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    @mcp.tool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; daily_snapshot&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(day: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&quot;&quot;One-call snapshot: sleep, HRV, load, and last workout for a day.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        row &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.snapshot_for(day)            &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# default: most recent day&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;day&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: row.day,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;resting_hr&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: row.resting_hr,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;hrv_ms&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: row.hrv_ms,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;sleep_hours&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;round&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(row.sleep_minutes &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 60&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;distance_miles&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;round&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(row.distance_meters &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1609.34&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;),  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# format server-side&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;training_load&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: row.training_load,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    @mcp.resource&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness://schema&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; schema&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&quot;&quot;The table shapes a client should know before querying.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.schema_as_markdown()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    @mcp.resource&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness://brief/latest&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; latest_brief&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.latest_brief_markdown()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    @mcp.prompt&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(title&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Coach&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; coach&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(day: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        snap &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; daily_snapshot(day)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;You are my training coach. Today&apos;s snapshot:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;snap&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;Give me one focus for today.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Formatting miles inside &lt;code&gt;daily_snapshot&lt;/code&gt; is deliberate. The client should never have to know your meters-to-miles convention, and every consumer gets the same answer because the conversion lives in one place.&lt;/p&gt;
&lt;h2&gt;One server, two transports&lt;/h2&gt;
&lt;p&gt;The easy mistake when adding a new way to reach your tools is to stand up a second server for the new transport. That immediately gives you two copies of every schema and handler to keep in sync. MCP supports multiple transports against the same server precisely so you do not have to (&lt;a href=&quot;https://modelcontextprotocol.io/specification/draft/basic/transports/streamable-http&quot;&gt;MCP: Streamable HTTP transport&lt;/a&gt;). stdio is what a local desktop client launches as a subprocess; streamable-HTTP is the network-reachable endpoint. Both are doors into the same room.&lt;/p&gt;
&lt;p&gt;Build the server once, then choose how to serve it. For HTTP you mount the SDK’s already-wired ASGI app into Starlette, wrapping the session manager in a lifespan so it starts and stops cleanly.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/transports.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; contextlib&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; starlette.applications &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Starlette&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; starlette.routing &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Mount&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.server &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; register_read_tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.writes &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; register_write_tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.server.fastmcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; build_full_server&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; FastMCP:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    mcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, json_response&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    register_read_tools(mcp)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    register_write_tools(mcp)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; http_app&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(mcp: FastMCP) -&gt; Starlette:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    @contextlib.asynccontextmanager&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; lifespan&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(app: Starlette):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.session_manager.run():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            yield&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # streamable_http_app() already serves on /mcp, so mount it at the root.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Mounting at &quot;/mcp&quot; would double-prefix the path to /mcp/mcp (a 404).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Starlette(routes&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[Mount(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, app&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;mcp.streamable_http_app())], lifespan&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;lifespan)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __name__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # stdio: the client spawns this process and speaks over stdin/stdout.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    build_full_server().run(transport&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;stdio&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One definition of &lt;code&gt;daily_snapshot&lt;/code&gt;, reachable two ways. A new tool shows up on both transports at once, and there is no second implementation to drift.&lt;/p&gt;
&lt;h2&gt;Fence the write surface structurally&lt;/h2&gt;
&lt;p&gt;Adding write tools to the same server the brief loop uses created a real risk. A brief generation should never mutate my data, but now the tools that can mutate it live in the same process. The weak fix is a comment that says “the brief code just will not call the write tools.” A comment survives exactly until some future code, or some agent, ignores it.&lt;/p&gt;
&lt;p&gt;The durable fix is to make the write tools unreachable from the brief path, so the rule is enforced by structure rather than discipline. This is the principle of least privilege applied at construction time: give the brief loop the minimum capability it needs and nothing more (&lt;a href=&quot;https://owasp.org/www-community/controls/Least_Privilege_Principle&quot;&gt;OWASP: Least Privilege Principle&lt;/a&gt;). I split registration into read and write halves, then build a separate read-only server instance that never registers the write half.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/writes.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.server.fastmcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; register_write_tools&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(mcp: FastMCP) -&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    @mcp.tool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; log_observation&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(note: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&quot;&quot;Append a coach/athlete observation.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: db.insert_observation(note)}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    @mcp.tool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; add_manual_workout&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(kind: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, minutes: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, distance_miles: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0.0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&quot;&quot;Record a workout that Garmin did not capture; feeds training load.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: db.insert_workout(kind, minutes, distance_miles)}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/brief.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.server &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; register_read_tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.server.fastmcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; build_read_only_server&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; FastMCP:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;The server the brief loop is allowed to touch. Write tools are never registered.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    mcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness-read&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    register_read_tools(mcp)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp                      &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# log_observation / add_manual_workout do not exist here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; run_brief&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    mcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_read_only_server()  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# handed only the read-only capability set&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # _tool_manager is a private SDK attribute; the public async equivalent is&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # `await mcp.list_tools()`. get_tool returns a Tool descriptor, not data.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    snapshot_tool &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp._tool_manager.get_tool(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;daily_snapshot&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; synthesize(snapshot_tool)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# synthesize() is your own reader; no path here reaches a write tool&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The brief loop cannot call a write tool because the instance it holds never registered one. The safe thing is the only available thing.&lt;/p&gt;
&lt;h2&gt;Secure a localhost server&lt;/h2&gt;
&lt;p&gt;A server bound to your own machine still needs locks. The MCP transport spec is explicit: servers MUST validate the &lt;code&gt;Origin&lt;/code&gt; header to prevent DNS rebinding, SHOULD bind to 127.0.0.1 rather than 0.0.0.0, and SHOULD authenticate (&lt;a href=&quot;https://modelcontextprotocol.io/specification/draft/basic/transports/streamable-http&quot;&gt;MCP: Streamable HTTP transport&lt;/a&gt;). DNS rebinding is the attack a “it’s only local” server tends to skip: a page you visit resolves an attacker-controlled hostname to 127.0.0.1, sidestepping the same-origin policy so the browser can talk to your local port (&lt;a href=&quot;https://en.wikipedia.org/wiki/DNS_rebinding&quot;&gt;DNS rebinding&lt;/a&gt;). Validating Origin and binding to loopback closes that door.&lt;/p&gt;
&lt;p&gt;My quality gate caught two execution-only gotchas here that source review missed. One was the missing Origin check. The other was a catch-all route registered before the MCP mount, which quietly shadowed it so nothing connected at all. Order the middleware and the mount with both in mind.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/secure.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os, hmac&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; starlette.middleware.base &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; BaseHTTPMiddleware&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; starlette.responses &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; JSONResponse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Browser Origin headers carry the port, so list the exact origin a client uses,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# e.g. http://localhost:8765, not just the scheme+host.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;ALLOWED_ORIGINS &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;http://localhost&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;http://127.0.0.1&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;http://localhost:8765&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;http://127.0.0.1:8765&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# BaseHTTPMiddleware buffers responses and breaks SSE streaming; it is safe here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# only because the server runs with json_response=True (no SSE stream to interrupt).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; GuardMiddleware&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(BaseHTTPMiddleware):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; dispatch&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self, request, call_next):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        origin &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; request.headers.get(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;origin&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; origin &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; origin &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ALLOWED_ORIGINS:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; JSONResponse({&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;bad origin&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}, status_code&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;403&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# DNS-rebinding guard&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # Read the token lazily so an unset env var fails the request, not import.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        token &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os.environ.get(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;FITNESS_MCP_TOKEN&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        auth &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; request.headers.get(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;authorization&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; hmac.compare_digest(auth, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Bearer &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;token&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; JSONResponse({&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;unauthorized&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}, status_code&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;401&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; call_next(request)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __name__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; uvicorn&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.transports &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_full_server, http_app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    app &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; http_app(build_full_server())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    app.add_middleware(GuardMiddleware)               &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# runs before the MCP mount handles the request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    uvicorn.run(app, host&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, port&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;8765&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)     &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# loopback only, not 0.0.0.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;stdio mode skips the token because the client owns the subprocess and there is no network surface. HTTP mode gets the bearer gate plus the Origin and loopback defenses.&lt;/p&gt;
&lt;h2&gt;Use it from a client&lt;/h2&gt;
&lt;p&gt;For a desktop MCP client, point it at the stdio entry point. The client launches the process and speaks the protocol over its stdin and stdout (&lt;a href=&quot;https://modelcontextprotocol.io/docs/develop/connect-local-servers&quot;&gt;MCP: Connect to local servers&lt;/a&gt;).&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;mcpServers&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    &quot;fitness&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      &quot;command&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;python&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      &quot;args&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;-m&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness.transports&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      &quot;env&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;&quot;FITNESS_DB&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;/home/me/fitness.db&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After restarting the client, the &lt;code&gt;coach&lt;/code&gt; prompt and the &lt;code&gt;daily_snapshot&lt;/code&gt; tool appear. Asking “what’s my focus today?” triggers a &lt;code&gt;tools/call&lt;/code&gt;, and the tool returns the same structured snapshot you defined, for example:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;day&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;2026-06-17&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;resting_hr&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;48&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;hrv_ms&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;72&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;sleep_hours&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;7.4&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;distance_miles&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;5.2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;training_load&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;moderate&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The client now reads &lt;code&gt;fitness://schema&lt;/code&gt; to learn the shape, then calls the tool, with no SQL on the client side and no second API to maintain.&lt;/p&gt;
&lt;h2&gt;Verify it&lt;/h2&gt;
&lt;p&gt;The two decisions worth defending are testable. Assert that the brief server cannot reach a write tool, and that a bad Origin is rejected. Both became regression tests so the fence and the guard cannot silently rot.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_mcp_safety.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; starlette.testclient &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; TestClient&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.brief &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_read_only_server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.transports &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; build_full_server, http_app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.secure &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; GuardMiddleware&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; tool_names&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(mcp) -&gt; set[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {t.name &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp._tool_manager.list_tools()}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_brief_server_has_no_write_tools&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    names &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tool_names(build_read_only_server())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;daily_snapshot&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; names&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;log_observation&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; names&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;add_manual_workout&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; names&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_bad_origin_is_rejected&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(monkeypatch):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setenv(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;FITNESS_MCP_TOKEN&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;test-token&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    app &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; http_app(build_full_server())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    app.add_middleware(GuardMiddleware)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    client &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; TestClient(app)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    resp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; client.post(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;/mcp&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, headers&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;origin&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;http://evil.example&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.status_code &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 403&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first test fails the moment someone registers a write tool on the brief path. The second fails if the Origin guard is removed or the catch-all route shadows the mount again.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;This is the setup for a bigger inversion. Once the agent can both read and write everything over MCP, the synthesis no longer needs to run inside the server at all; the brief loop can move out to an agent that talks to the same tools any other client uses. That becomes the agent-first rearchitecture in v0.5.0.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/news/model-context-protocol&quot;&gt;Anthropic: Introducing the Model Context Protocol&lt;/a&gt; — a standard surface of tools, resources, and prompts any client can reach.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/modelcontextprotocol/python-sdk&quot;&gt;MCP Python SDK&lt;/a&gt; — FastMCP decorators for tools, resources, and prompts, and mounting the server into an ASGI app.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://modelcontextprotocol.io/specification/draft/basic/transports/streamable-http&quot;&gt;MCP: Streamable HTTP transport&lt;/a&gt; — servers MUST validate Origin, SHOULD bind to localhost, and SHOULD authenticate.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/DNS_rebinding&quot;&gt;DNS rebinding&lt;/a&gt; — how a browser can reach a locally-bound server through an attacker-controlled name.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://owasp.org/www-community/controls/Least_Privilege_Principle&quot;&gt;OWASP: Least Privilege Principle&lt;/a&gt; — give a component the minimum capability it needs to limit blast radius.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://modelcontextprotocol.io/docs/develop/connect-local-servers&quot;&gt;MCP: Connect to local servers&lt;/a&gt; — pointing a desktop client at a local stdio server with command and args.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Make the fitness MCP the primary interface (#21) (#22) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/dbe865a1e00e7cde590019b9b5391a9cec200318&quot;&gt;dbe865a&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Guarantees the model can&apos;t break: shipping AI training plans design-first</title><link>https://natejswenson.com/devlog/local-fitness/2026-06-15/</link><guid isPermaLink="true">https://natejswenson.com/devlog/local-fitness/2026-06-15/</guid><description>I shipped goal-driven training plans where the AI can draft but never decide, a single active plan is enforced by the database itself, and adherence is graded off the last day data actually synced. The throughline: put the things you can&apos;t afford to get wrong into structure, not into prompts or discipline.</description><pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;v0.2.0 turns local-fitness into something that can actually coach toward a race. You pick a goal (5K, 10K, half, full, or custom), a race date, and a target time. The coach drafts a periodized plan from your Garmin history, you riff on it in chat until it fits, and then you commit. Once committed, the plan gets its own Training Plan tab: a schedule with per-day adherence verdicts, planned-versus-actual weekly mileage, a fitness trajectory chart, a predicted finish, and a fold into the daily brief so the plan shows up where you already look.&lt;/p&gt;
&lt;p&gt;The features are the easy part to describe. The part worth writing about is how the risky bits were built, because a full design doc went through an adversarial security and correctness review before any code existed, which closed four Critical and six High findings at the design stage. Three of the decisions that came out of that review are reusable far beyond a running app: the AI can draft but never activate, a single active plan is guaranteed by the database itself, and adherence is graded against the data, not the calendar. Here is the end-to-end build, plus a fourth lesson that came later from a latency optimization I designed, built, measured, and then deleted.&lt;/p&gt;
&lt;h2&gt;Setup: the schema that makes the rules enforceable&lt;/h2&gt;
&lt;p&gt;All three guarantees hang on one column. A plan is born as a &lt;code&gt;draft&lt;/code&gt;, becomes &lt;code&gt;active&lt;/code&gt; when a human commits it, and is &lt;code&gt;archived&lt;/code&gt; when it gets replaced. The status column is the hinge, and the database is where two of the three rules get their teeth.&lt;/p&gt;
&lt;p&gt;PostgreSQL supports a partial index, an index built over only the rows that satisfy a &lt;code&gt;WHERE&lt;/code&gt; predicate. Make it unique and you get uniqueness enforced over just that subset, with no constraint on the rest (&lt;a href=&quot;https://www.postgresql.org/docs/current/indexes-partial.html&quot;&gt;PostgreSQL: Partial Indexes&lt;/a&gt;). That is exactly the shape of “at most one active plan per user,” so it goes in the schema from the start rather than into a runtime check that some future caller can forget.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;-- A plan is born as a draft, becomes active when a human commits it,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;-- and is archived when it is replaced. The status column is the hinge.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;CREATE&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; TABLE&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; training_plans&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    id          &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;BIGSERIAL&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;   PRIMARY KEY&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    user_id     &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;BIGINT&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      NOT NULL&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; REFERENCES&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; users (id),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    goal        &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;TEXT&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;        NOT NULL&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;-- &apos;5k&apos; | &apos;10k&apos; | &apos;half&apos; | &apos;full&apos; | &apos;custom&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    race_date   &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;DATE&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;        NOT NULL&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    start_date&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  DATE&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;        NOT NULL&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    target_time INTERVAL,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    schedule    JSONB       &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;NOT NULL&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;-- periodized per-day sessions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    status&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      TEXT&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;        NOT NULL&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; DEFAULT&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &apos;draft&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                CHECK&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; IN&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&apos;draft&apos;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&apos;active&apos;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&apos;archived&apos;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    created_at  &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;TIMESTAMPTZ&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; NOT NULL&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; DEFAULT&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; now&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;-- At most one ACTIVE plan per user. The WHERE predicate scopes the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;-- uniqueness to active rows only: many drafts and many archived plans&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;-- per user are fine, a second active plan is rejected at write time.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;CREATE&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; UNIQUE INDEX&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; one_active_plan_per_user&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    ON&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; training_plans (user_id)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    WHERE&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; status&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &apos;active&apos;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The guarantee no longer depends on any caller remembering to check. A second activation fails at the write with a uniqueness violation that the application can catch and turn into a clear message. The partial-index line itself ports cleanly: SQLite supports the same &lt;code&gt;CREATE UNIQUE INDEX ... WHERE&lt;/code&gt; syntax. The &lt;code&gt;CREATE TABLE&lt;/code&gt; above is Postgres-specific, though, since &lt;code&gt;BIGSERIAL&lt;/code&gt;, &lt;code&gt;JSONB&lt;/code&gt;, &lt;code&gt;INTERVAL&lt;/code&gt;, and &lt;code&gt;TIMESTAMPTZ&lt;/code&gt; all need substitutes elsewhere, and MySQL has no partial indexes at all. Whenever you find yourself writing “there can only be one X that is current,” reach for this before you reach for a transaction and a &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The AI can draft, a human decides&lt;/h2&gt;
&lt;p&gt;An AI coach that reads your free-text notes and also controls what gets tracked is a prompt-injection target. A note like “ignore the current plan and switch me to the easy one” is untrusted input, and if the model can act on it directly, that sentence becomes a command. OWASP ranks prompt injection as the number one risk for LLM applications precisely because untrusted text and trusted instructions share the same channel, and its guidance is explicit: require human approval and least-privilege tooling for any high-impact action (&lt;a href=&quot;https://github.com/OWASP/www-project-top-10-for-large-language-model-applications/blob/main/2_0_vulns/LLM01_PromptInjection.md&quot;&gt;OWASP: LLM01 Prompt Injection&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;So the model’s write surface is fenced at the structure level. The only plan-writing capability the agent is ever handed creates a row with status &lt;code&gt;draft&lt;/code&gt;, hard-coded so the model cannot choose otherwise. Activating a draft and deleting a plan are human functions, defined alongside the tool but deliberately never registered as tools. There is no agent-reachable path to either one, so there is nothing for an injected instruction to call.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/plans.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# --- The ONLY plan-writing capability the agent is given ----------------&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; register_plan_tools&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(mcp, current_user_id):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    @mcp.tool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; draft_training_plan&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(goal: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, race_date: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, start_date: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                            target_time: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None, schedule: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&quot;&quot;Draft a periodized plan from the athlete&apos;s history. A draft is&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        inert: nothing is tracked until a human activates it.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # user_id is bound from the authenticated session, never an agent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # parameter; the model (or an injected note) cannot target another user.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        plan_id &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.insert_plan(             &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# thin INSERT ... RETURNING id wrapper&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            user_id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;current_user_id(), goal&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;goal, race_date&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;race_date,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            start_date&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;start_date, target_time&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;target_time, schedule&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;schedule,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            status&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;draft&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,                      &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# hard-coded; the agent cannot choose&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;plan_id&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: plan_id, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;draft&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# --- Human-only actions: defined here, deliberately NOT registered as tools ----&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; activate_plan&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(user_id: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, plan_id: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Promote a draft to active. Invoked from the UI commit button, never&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    from the agent. A second active plan is refused by the partial index.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    row &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.execute(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;UPDATE training_plans SET status = &apos;active&apos; &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;WHERE id = &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;%s&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; AND user_id = &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;%s&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; AND status = &apos;draft&apos; &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;RETURNING id&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        (plan_id, user_id),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; row &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None:                              &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# no draft matched: already&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;no_draft&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;                        # active, archived, or wrong owner&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;activated&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; delete_plan&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(user_id: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, plan_id: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Remove a plan. Human action, enforced in code, not a tool.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    db.execute(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;DELETE FROM training_plans WHERE id = &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;%s&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; AND user_id = &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;%s&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        (plan_id, user_id),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A poisoned note can, at worst, make the coach draft a bad plan that I then read and decline. It can never silently change what’s being tracked. The general move is to make the dangerous capability unreachable from the untrusted path, instead of trusting the model to decline it.&lt;/p&gt;
&lt;h2&gt;Grade against the data, not the calendar&lt;/h2&gt;
&lt;p&gt;Adherence verdicts have a subtle correctness trap. If you grade a plan by wall-clock time, then the moment today’s date arrives you mark today’s session as missed, even though Garmin hasn’t synced today’s activity yet. Sync lag is normal, and a coach that says “you missed your run” because the data is three hours behind is worse than useless; it trains you to ignore it.&lt;/p&gt;
&lt;p&gt;This is the same distinction stream-processing systems draw between event time, when something actually happened, and processing time, when the system got around to looking (&lt;a href=&quot;https://www.databricks.com/blog/2017/05/08/event-time-aggregation-watermarking-apache-sparks-structured-streaming.html&quot;&gt;Databricks: Event-time Aggregation and Watermarking&lt;/a&gt;). Their answer is a watermark, a moving threshold that trails the latest data the system has actually seen, so computation waits for the data instead of racing the clock. local-fitness grades off the same idea, the data frontier: the last day Garmin actually synced. Days at or before the frontier are gradable; days after it are pending, never failing. Days before the plan starts and rest days each get their own explicit verdict, so the model is never handed an undefined case to improvise around.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/adherence.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; datetime &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; data_frontier&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(user_id: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; date &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;The last day Garmin actually synced, or None for a never-synced athlete.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    Days after this are not yet gradable; grading them would race the clock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    instead of the data.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.last_synced_day(user_id)         &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# SELECT max(day) FROM activities WHERE user_id=%s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; grade_plan&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(user_id: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, plan_id: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    frontier &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; data_frontier(user_id) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;or&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; date.min   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# no history yet: every day stays pending&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    plan &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.get_plan(user_id, plan_id)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    verdicts &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # plan.start_date and frontier come back as date objects from the driver;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # only the JSONB schedule date is a string, so it is the one parsed by hand.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; day &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; plan.schedule:                  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# each: {date, planned_miles, kind}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        d &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; date.fromisoformat(day[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; d &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; plan.start_date:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;not_started&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;            # plan hasn&apos;t begun; nothing expected yet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        elif&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; d &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; frontier:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;pending&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;                # data not in yet, never &quot;missed&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        elif&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; day[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;kind&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;rest&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;rest&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;                   # nothing to hit, nothing to miss&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;            # COALESCE matters: a missed run has zero matching rows, and a bare&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;            # SUM over zero rows returns NULL (not 0), so the wrapper coalesces.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            actual &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.actual_miles(user_id, d) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;or&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   # SELECT COALESCE(SUM(miles), 0) ... WHERE day=%s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; actual &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;missed&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            elif&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; actual &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; day[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;planned_miles&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0.9&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;hit&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                verdict &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;short&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        verdicts.append({&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: day[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;verdict&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: verdict})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; verdicts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The verdict is a fact about the data, never an artifact of when you happened to open the app. The same frontier feeds the daily brief, so the brief always references the active plan with a defined state for today rather than a gap the model fills in differently each run.&lt;/p&gt;
&lt;h2&gt;Use it: draft, then commit&lt;/h2&gt;
&lt;p&gt;The two paths are intentionally asymmetric. The agent path produces an inert draft and stops there. The human path is the only thing that can make a plan real, and the database backstops it.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/server.py: wire the draft tool onto an MCP server.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; mcp.server.fastmcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.plans &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; register_plan_tools, activate_plan&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;mcp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FastMCP(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;local-fitness&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# `session` here is your web framework&apos;s per-request session (Flask&apos;s session, a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# FastAPI dependency, etc.); the point is user_id is resolved server-side and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# never passed in by the agent.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;register_plan_tools(mcp, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: session.user_id)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# user_id bound from the session&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Agent path: in chat, the model invokes the draft_training_plan tool over MCP with a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# schedule it built from your Garmin history. This is all the agent can do. The&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# equivalent direct call, shown only for clarity (the model never calls Python directly):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;#   draft_training_plan(goal=&quot;half&quot;, race_date=&quot;2026-09-20&quot;, start_date=&quot;2026-06-16&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;#                       target_time=&quot;01:45:00&quot;, schedule=coach_built_schedule)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;#   -&gt; {&quot;plan_id&quot;: 318, &quot;status&quot;: &quot;draft&quot;}  # inert until a human commits&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Human path: the athlete reads the draft, then clicks Commit in the UI.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;activate_plan(user_id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;42&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, plan_id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;318&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)       &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# now tracked; one active guaranteed&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# A second commit while another plan is active fails at the database:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; psycopg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;try&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    activate_plan(user_id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;42&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, plan_id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;319&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;except&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; psycopg.IntegrityError:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # UniqueViolation from one_active_plan_per_user; surfaced to the user&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # as &quot;you already have an active plan.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activation triggers the grader against the data frontier, so the Training Plan tab fills in with &lt;code&gt;hit&lt;/code&gt;, &lt;code&gt;short&lt;/code&gt;, &lt;code&gt;missed&lt;/code&gt;, &lt;code&gt;rest&lt;/code&gt;, &lt;code&gt;not_started&lt;/code&gt;, and &lt;code&gt;pending&lt;/code&gt; verdicts that match reality rather than the clock.&lt;/p&gt;
&lt;h2&gt;Verify the guarantees&lt;/h2&gt;
&lt;p&gt;Two of these decisions are exactly the kind that rot silently, so both became regression tests. One asserts that the database refuses a second active plan; the other asserts that the activate function is not reachable through anything the agent is given.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_plan_guarantees.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; psycopg, pytest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.plans &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; activate_plan, register_plan_tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fake_mcp is a recording stub: its .tool() decorator appends each registered&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# function to .tools as an object exposing .name, so the test can assert what&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# actually got wired (real FastMCP does not expose tools this way).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@pytest.fixture&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; fake_mcp&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; FakeMCP&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; __init__&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.tools &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; tool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(self):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; register&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(fn):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                self&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.tools.append(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Tool&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, (), {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: fn.__name__})())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fn&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; register&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; FakeMCP()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_second_active_plan_is_rejected&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(db_conn, two_drafts):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    a, b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; two_drafts                          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# two draft plans, same user&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    activate_plan(user_id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, plan_id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;a)        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# first one is fine&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pytest.raises(psycopg.IntegrityError):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        activate_plan(user_id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, plan_id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;b)    &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# partial index refuses the second&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_agent_cannot_reach_activate&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(fake_mcp):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    register_plan_tools(fake_mcp, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# everything the agent is given&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    names &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {t.name &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fake_mcp.tools}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;draft_training_plan&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; names&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;activate_plan&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; names        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# human-only, never a tool&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;delete_plan&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; names&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first test fails if the partial index is dropped or weakened. The second fails the moment someone registers &lt;code&gt;activate_plan&lt;/code&gt; as a tool, which is the only way an injected note could ever reach it. The guarantees are now load-bearing structure that the suite defends, not promises in a system prompt.&lt;/p&gt;
&lt;h2&gt;Output-bound, not round-trip-bound&lt;/h2&gt;
&lt;p&gt;The last lesson cost the most to learn, which is what made it worth keeping. After the feature shipped I lined up five efficiency ideas for the daily brief and ran them through adversarial review in two phases. Phase A shipped cleanly: compact JSON tool output, a three-way Haiku / Sonnet / Opus chat toggle defaulting to Sonnet, and a regression test that keeps the system prompt cache-stable. Phase B was the obvious big win. The brief was making roughly eight sequential tool round-trips to gather its data, so I designed a path that pre-computes all of that in-process and hands it to the model in one shot, collapsing eight trips into one.&lt;/p&gt;
&lt;p&gt;The design’s own gate demanded a live before-and-after measurement before merging, and the measurement said no. Tool-driven was about 211 seconds; pre-fetched was about 255, slightly worse. The brief is output-bound, because most of the wall-clock is the model writing the brief, not the app fetching data, so removing round-trips optimized a part of the total that was never the bottleneck. This is Amdahl’s law in plain clothes; the speedup from improving one component is capped by the fraction of total time that component actually occupies (&lt;a href=&quot;https://en.wikipedia.org/wiki/Amdahl%27s_law&quot;&gt;Wikipedia: Amdahl’s law&lt;/a&gt;). It is also the standard argument for measuring before optimizing rather than reasoning from the call graph (&lt;a href=&quot;https://www.qt.io/quality-assurance/blog/premature-optimization&quot;&gt;Qt: Premature Optimization&lt;/a&gt;). Phase B was reverted, but the A/B run paid for itself by exposing the undefined plan-status case that the data-frontier grader now handles directly.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The immediate follow-up is to open the PR for the stacked branch and get this landed. The open question I want to chase is why the brief’s plan-fold instruction needed babysitting at all: the prompt carries several mandates at once, and following them is uneven, which points at how to structure multi-mandate instructions so each one lands reliably. A live security siege against the running container held on auth, injection, rate-limiting, and the single-active invariant; I hardened the two things it flagged (stopped advertising the server stack, added HSTS).&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/OWASP/www-project-top-10-for-large-language-model-applications/blob/main/2_0_vulns/LLM01_PromptInjection.md&quot;&gt;OWASP: LLM01 Prompt Injection&lt;/a&gt; — ranks prompt injection first and recommends human approval and least-privilege tooling for high-impact actions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/indexes-partial.html&quot;&gt;PostgreSQL: Partial Indexes&lt;/a&gt; — a unique index over a &lt;code&gt;WHERE&lt;/code&gt;-scoped subset enforces uniqueness on just those rows.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.databricks.com/blog/2017/05/08/event-time-aggregation-watermarking-apache-sparks-structured-streaming.html&quot;&gt;Databricks: Event-time Aggregation and Watermarking&lt;/a&gt; — event time versus processing time, and watermarks that trail the data actually seen.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Amdahl%27s_law&quot;&gt;Wikipedia: Amdahl’s law&lt;/a&gt; — the speedup from improving one part is bounded by the fraction of total time it occupies.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.qt.io/quality-assurance/blog/premature-optimization&quot;&gt;Qt: Premature Optimization&lt;/a&gt; — measure to find the real bottleneck before optimizing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat: goal-driven training plans end to end, shipped as v0.2.0&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Reach is earned in dwell time: encoding LinkedIn&apos;s feed rules into a posting tool</title><link>https://natejswenson.com/devlog/ghostwriter/v0.3.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/ghostwriter/v0.3.0/</guid><description>ghostwriter v0.3.0 turned a pile of researched LinkedIn reach rules into config the skill reads on every draft, and added native carousels by rendering HTML slides into a PDF that LinkedIn shows as a swipeable document. Here is the full build: the playbook as data, the slide-to-PDF render, the documents-API upload, and the check that runs before anything publishes.</description><pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;ghostwriter writes LinkedIn posts in my voice and publishes them after I approve. v0.3.0 does three things. It bakes a researched, sourced reach playbook into the skill as config the tool reads on every run, instead of advice I try to remember. It adds a &lt;code&gt;flow&lt;/code&gt; diagram card type. And it ships end-to-end carousel support: a renderer that screenshots HTML slides and stitches them into a PDF, plus a publisher path that uploads that PDF through LinkedIn’s documents API, which LinkedIn then renders as a swipeable carousel in the feed. I verified the whole path against the real API. As usual I kept treating the skill like code, not a prompt: 121 tests at 100% coverage, shellcheck clean.&lt;/p&gt;
&lt;p&gt;The reach rules are concrete. Put the hook up top, before the “see more” fold (~210 chars on desktop, less on mobile). Keep the body around 900 to 1500 characters. Optimize for saves over likes. Never put an external link in the post body. Run a “golden hour” engagement routine right after posting. The part worth writing about is why those rules are true, and then the small chain of browser and API plumbing that makes a native carousel possible at all. This is the end-to-end build: the playbook as data the generator reads, the slide-to-PDF render, the upload, and the verification gate.&lt;/p&gt;
&lt;h2&gt;What the feed actually measures: dwell time, not clicks&lt;/h2&gt;
&lt;p&gt;The single most useful thing to understand about LinkedIn ranking is that how long people spend on your post is a first-class quality signal, weighted heavily alongside explicit actions like a tap on “like.” LinkedIn’s own engineering team published this: they rank partly on dwell time because it’s measurable on every viewed post including passive reads, it’s a real-valued signal instead of the binary yes or no of a like, and it dodges the “click bounce” problem where someone opens a thing and immediately backs out (&lt;a href=&quot;https://www.linkedin.com/blog/engineering/feed/understanding-feed-dwell-time&quot;&gt;LinkedIn Engineering: Understanding dwell time&lt;/a&gt;). They model the likelihood that a post is skipped quickly and down-rank posts likely to be abandoned.&lt;/p&gt;
&lt;p&gt;Once you accept that, the rest of the rules stop looking like folklore. The hook goes near the top because the opening slice is what has to earn the “see more” tap, and the tap is the first chunk of dwell time. The exact fold is an observed heuristic that varies by client: I use ~210 characters as a desktop figure, but mobile truncates earlier (often around 140), so treat the number as a knob, not a law. Posts land in the 900 to 1500 character range because they need to be long enough to hold attention but short enough to finish. “Optimize for saves over likes” is really “optimize for the signal that means this was worth coming back to,” which is a far stronger proxy for value than a reflexive like. The “golden hour” routine falls out of the same model: early engagement is the test LinkedIn uses to decide whether to widen distribution, so a short burst of genuine replies right after posting is the difference between a post that travels and one that quietly stalls.&lt;/p&gt;
&lt;p&gt;The “never put a link in the body” rule has the same root. The feed is optimized to keep people on the platform, so a post that sends them off it gets deprioritized; LinkedIn’s marketing guidance says as much and recommends putting the link in the first comment instead (&lt;a href=&quot;https://www.linkedin.com/top-content/marketing/linkedin-content-and-ads/do-links-lower-linkedin-post-reach/&quot;&gt;LinkedIn: Do links lower post reach&lt;/a&gt;). An outbound link is a dwell-time leak. None of this is a trick; it’s aligning what you publish with what the ranker can actually see.&lt;/p&gt;
&lt;h2&gt;The playbook as data the generator reads&lt;/h2&gt;
&lt;p&gt;Here’s the design decision I care about most. All of the above lives in config the skill loads on every run, rather than in my head or buried in a prompt I rewrote once and forgot. The research is encoded as machine-readable rules the tool applies automatically, each with the source that justifies it, so the rules stay auditable. When LinkedIn changes how the feed behaves, I edit one file and every future draft inherits the new rule.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# voice/playbook.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Each rule carries the research that justifies it, so the config is auditable:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# open the file, read the claim, follow the source, challenge it if it&apos;s stale.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclasses &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; dataclass, field&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@dataclass&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(frozen&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; ReachPlaybook&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    hook_max_chars: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 210&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # observed desktop &quot;see more&quot; fold; mobile truncates earlier (~140), so tune per surface&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    body_min_chars: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 900&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # long enough to hold attention&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    body_max_chars: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1500&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;       # short enough to finish in one sitting&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    allow_body_links: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; False   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# outbound links leak dwell time; move to comment 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    optimize_for: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;saves&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;      # saves imply real dwell + intent to return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    sources: dict[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; field(default_factory&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;dwell_time&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;https://www.linkedin.com/blog/engineering/feed/understanding-feed-dwell-time&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;links&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;https://www.linkedin.com/top-content/marketing/linkedin-content-and-ads/do-links-lower-linkedin-post-reach/&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;PLAYBOOK &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ReachPlaybook()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The generator then enforces the playbook on the draft before I ever see it, so a reach-suppressing mistake never reaches the approval step. The check returns structured violations rather than throwing, so the tool can show me exactly what to fix.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# voice/lint.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; voice.playbook &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; PLAYBOOK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;LINK &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.compile(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&quot;https?://&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, re.IGNORECASE)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; lint_draft&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(body: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Apply the reach playbook to a draft. Empty list means it&apos;s clean.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    issues: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # LinkedIn&apos;s &quot;see more&quot; fold is character-based across the whole post, so&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # the visible hook is the first hook_max_chars characters, newlines included.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    hook &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; body[: PLAYBOOK.hook_max_chars &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(body) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; PLAYBOOK.hook_max_chars &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; hook:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # No paragraph break inside the visible window: the opening line runs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # past the fold, so the hook gets cut mid-thought before &quot;see more&quot;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        issues.append(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;no line break within the first &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;PLAYBOOK.hook_max_chars&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; chars; &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;the hook runs past the &apos;see more&apos; fold&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (PLAYBOOK.body_min_chars &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(body) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; PLAYBOOK.body_max_chars):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        issues.append(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;body is &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(body)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; chars; aim for &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;PLAYBOOK.body_min_chars&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;PLAYBOOK.body_max_chars&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; PLAYBOOK.allow_body_links &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; LINK.search(body):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        issues.append(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;link in body suppresses reach; move it to the first comment&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; issues&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A playbook that only exists as my memory degrades the moment I forget a rule under deadline. A playbook that’s a file the tool enforces does not have that failure mode, and it makes every rule challengeable by anyone who reads the config.&lt;/p&gt;
&lt;h2&gt;Rendering slides into a PDF carousel&lt;/h2&gt;
&lt;p&gt;The carousel feature comes down to one constraint: LinkedIn’s document posts accept a PDF and render each page as one swipeable slide. PDF is the format that survives the round trip because its layout is locked, so what you design is what shows up. The pipeline is therefore: design each slide as HTML, render each slide to an image, and stitch those images into a multi-page PDF where one page equals one slide.&lt;/p&gt;
&lt;p&gt;The render clips a fixed rectangle, so each slide’s HTML has to be sized to exactly that rectangle with zeroed body margins; otherwise the clip crops content or leaves blank gutters. A minimal slide looks like this, with &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; matching the &lt;code&gt;SLIDE_W&lt;/code&gt;/&lt;code&gt;SLIDE_H&lt;/code&gt; constants the renderer clips to.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;&amp;#x3C;!-- slide_1.html --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;!&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;DOCTYPE&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; html&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; charset&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      html&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { margin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; padding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;      /* Must match SLIDE_W x SLIDE_H in the renderer, or the clip won&apos;t line up. */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      .slide&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        width&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1080px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        height&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1350px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        box-sizing&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; border-box&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        justify-content&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        background&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #0a0a0a&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #fff&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 600&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 64px&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1.2&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; system-ui&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;sans-serif&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;slide&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Reach is earned in dwell time&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the render step I drive a headless browser. Start with the dependencies.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;python&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; venv&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; .venv&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;source&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; .venv/bin/activate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;pip&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; playwright&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; img2pdf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; pypdf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; requests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;playwright&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; chromium&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are two clean ways to get a PDF out of Chromium. The native one is Playwright’s &lt;code&gt;page.pdf()&lt;/code&gt;, which prints the page using &lt;code&gt;print&lt;/code&gt; CSS media and needs &lt;code&gt;print_background=True&lt;/code&gt; plus &lt;code&gt;-webkit-print-color-adjust: exact&lt;/code&gt; to keep backgrounds and brand colors from being stripped for “printing” (&lt;a href=&quot;https://playwright.dev/docs/api/class-page&quot;&gt;Playwright: page.pdf()&lt;/a&gt;; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust&quot;&gt;MDN: -webkit-print-color-adjust&lt;/a&gt;). The other, which is what ghostwriter does, is to screenshot each slide at a fixed pixel size and assemble the PNGs into a PDF. Screenshots use &lt;code&gt;screen&lt;/code&gt; media, so what you see in the browser is exactly what lands in the file, with no print-stylesheet surprises. For pixel-perfect, image-like slides, that predictability is worth more than &lt;code&gt;page.pdf()&lt;/code&gt;&apos;s pagination smarts.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# render_carousel.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tempfile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; img2pdf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; playwright.sync_api &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# LinkedIn renders each PDF page as one swipeable slide. A 4:5 portrait&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# frame (1080x1350) is the most mobile-friendly, so we capture at that size.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;SLIDE_W, SLIDE_H &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1080&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1350&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;SCALE &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # device_scale_factor: render at 2x for retina-crisp text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; render_slides_to_pdf&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(slide_html: list[Path], out_pdf: Path) -&gt; Path:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Screenshot each HTML slide, then stitch the images into one PDF.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    png_paths: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Write intermediate PNGs to a scratch dir so we never clobber files&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # next to the input HTML.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    tmp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(tempfile.mkdtemp(prefix&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;carousel_&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        browser &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p.chromium.launch()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # SCALE doubles the pixel density, so a SLIDE_W x SLIDE_H CSS clip&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # produces a (SLIDE_W*SCALE) x (SLIDE_H*SCALE) PNG.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; browser.new_page(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            viewport&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: SLIDE_W, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: SLIDE_H},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            device_scale_factor&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;SCALE,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; i, slide &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; enumerate&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(slide_html):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;            # wait_until=&quot;load&quot; is reliable; networkidle is discouraged and flaky.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            page.goto(slide.resolve().as_uri(), wait_until&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;load&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            page.evaluate(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;async () =&gt; { await document.fonts.ready; }&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# let web fonts settle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            png &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tmp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;slide_&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;.png&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;            # Clip to the exact frame so every slide is identically sized.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            page.screenshot(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                path&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(png),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                clip&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;y&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: SLIDE_W, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: SLIDE_H},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            png_paths.append(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(png))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        browser.close()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # One image becomes exactly one PDF page, which is exactly one slide.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    out_pdf.write_bytes(img2pdf.convert(png_paths))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; out_pdf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Uploading the PDF through LinkedIn’s documents API&lt;/h2&gt;
&lt;p&gt;A carousel is a document post, not an image or text post, so it takes a different upload path. LinkedIn’s documents API is a three-step handshake: register the upload to get a one-time URL and a document URN, PUT the raw bytes to that URL, then attach the returned URN to a post (&lt;a href=&quot;https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/documents-api&quot;&gt;LinkedIn: Documents API&lt;/a&gt;). The version header is mandatory and is a bare &lt;code&gt;YYYYMM&lt;/code&gt; string. One gotcha that isn’t obvious from the scope name: the &lt;code&gt;/rest/documents&lt;/code&gt; and &lt;code&gt;/rest/posts&lt;/code&gt; endpoints live under LinkedIn’s Community Management API product, which has to be explicitly enabled on your app. A plain Sign-In/Share app holding only the &lt;code&gt;w_member_social&lt;/code&gt; scope returns 403 at &lt;code&gt;initializeUpload&lt;/code&gt; until that product is approved.&lt;/p&gt;
&lt;p&gt;Two inputs come from your LinkedIn app, not from this code: &lt;code&gt;token&lt;/code&gt; is a member access token with the &lt;code&gt;w_member_social&lt;/code&gt; scope (read it from an env var, e.g. &lt;code&gt;os.environ[&amp;quot;LINKEDIN_TOKEN&amp;quot;]&lt;/code&gt;), and &lt;code&gt;author_urn&lt;/code&gt; is the posting member’s person URN in the form &lt;code&gt;urn:li:person:{id}&lt;/code&gt;, where &lt;code&gt;{id}&lt;/code&gt; is the &lt;code&gt;sub&lt;/code&gt; claim returned by the &lt;code&gt;/userinfo&lt;/code&gt; (OpenID) endpoint. That &lt;code&gt;/userinfo&lt;/code&gt; call needs the OpenID Connect scopes (&lt;code&gt;openid profile&lt;/code&gt;) on the token, a separate product enablement from &lt;code&gt;w_member_social&lt;/code&gt;, so request all three when you set the app up.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# publish_document.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; requests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;API &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;https://api.linkedin.com/rest&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; _headers&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(token: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;Authorization&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Bearer &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;token&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;LinkedIn-Version&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;202602&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# mandatory; bare YYYYMM, not a date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;X-Restli-Protocol-Version&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;2.0.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; publish_carousel&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(token: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, author_urn: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, pdf: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bytes&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, title: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    h &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _headers(token)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # 1) Register the upload. LinkedIn hands back a one-time URL and the URN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    #    that will represent this document everywhere it&apos;s reused.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    r &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; requests.post(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;API&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/documents?action=initializeUpload&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        headers&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;h,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        json&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;initializeUploadRequest&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;owner&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: author_urn}},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        timeout&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;30&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    r.raise_for_status()  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# surface the API error body, not a later KeyError&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    init &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; r.json()[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    upload_url, doc_urn &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; init[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;uploadUrl&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], init[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;document&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # 2) PUT the raw PDF bytes to that URL. No JSON wrapper, just the file.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    put &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; requests.put(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        upload_url, headers&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: h[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]}, data&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;pdf, timeout&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;60&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    put.raise_for_status()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # 3) Attach the document URN to a post. This is what makes the feed&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    #    render it as a swipeable carousel instead of a file attachment.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    post &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; requests.post(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;API&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/posts&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, headers&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;h, json&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;author&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: author_urn,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;commentary&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;visibility&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;PUBLIC&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;distribution&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;feedDistribution&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;MAIN_FEED&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;media&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: doc_urn, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: title}},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;lifecycleState&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;PUBLISHED&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    }, timeout&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;30&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    post.raise_for_status()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; doc_urn&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The document URN is reusable; once a PDF is uploaded, the same URN can back more than one post without re-uploading the bytes. The carousel is just that document wearing a swipe gesture.&lt;/p&gt;
&lt;h2&gt;Verify the PDF before you publish&lt;/h2&gt;
&lt;p&gt;The upload is the expensive, hard-to-undo step, so the check belongs before it, not after. The two things that actually break a carousel are silent: a render that dropped or duplicated a slide gives the wrong page count, and a slide that came out the wrong aspect ratio gets letterboxed in the feed. Both are cheap to assert from the finished PDF, and both became a gate the publisher runs before it ever calls the API.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# verify_carousel.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pypdf &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; PdfReader&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; render_carousel &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SLIDE_W, SLIDE_H, SCALE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Derive the expected page size from the render constants so the two can&apos;t drift.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# device_scale_factor=SCALE makes the screenshot (SLIDE_W*SCALE) x (SLIDE_H*SCALE)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# pixels. Chromium screenshots don&apos;t write a pHYs DPI chunk, so img2pdf falls&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# back to its 96-DPI default; PDF points are 1/72 inch, so each pixel becomes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 72/96 of a point.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;PT_PER_PX &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 72&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 96&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;EXPECTED_W &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SLIDE_W &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SCALE &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; PT_PER_PX   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 1080*2*0.75 = 1620.0 pt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;EXPECTED_H &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SLIDE_H &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SCALE &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; PT_PER_PX   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 1350*2*0.75 = 2025.0 pt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;TOLERANCE &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 2.0&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;                            # absorb rounding from the export&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Caveat: this size gate assumes no pHYs DPI chunk in the PNGs. A Chromium or&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# toolchain build that embeds DPI would change img2pdf&apos;s points-per-pixel and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# shift the page size, tripping the gate; re-derive PT_PER_PX if that happens.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; verify_carousel&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(pdf: Path, expected_slides: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Fail loudly before upload if the PDF is the wrong shape.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    pages &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; PdfReader(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(pdf)).pages&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(pages) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; expected_slides, (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;expected &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;expected_slides&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; slides, PDF has &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(pages)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; i, page &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; enumerate&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(pages):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        w, h &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(page.mediabox.width), &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(page.mediabox.height)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        assert&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; abs&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(w &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; EXPECTED_W) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; TOLERANCE, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;slide &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; width &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;w&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; != &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;EXPECTED_W&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        assert&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; abs&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(h &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; EXPECTED_H) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; TOLERANCE, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;slide &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; height &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;h&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; != &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;EXPECTED_H&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; h &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; w, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;slide &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; is not portrait (&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;w&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;h&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # 4:5 must be taller than wide&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wiring the pieces together, the publish path is render, verify, then upload, and the verify step is the one allowed to stop it.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; requests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; render_carousel &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; render_slides_to_pdf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; verify_carousel &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; verify_carousel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; publish_document &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; publish_carousel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Two inputs come from your LinkedIn app, not from this code (see the section above).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;token &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os.environ[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;LINKEDIN_TOKEN&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# member token with w_member_social + openid profile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;sub &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; requests.get(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;https://api.linkedin.com/v2/userinfo&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    headers&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Bearer &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;token&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    timeout&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;30&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).json()[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;sub&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;author_urn &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;urn:li:person:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;sub&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;slides &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [Path(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;slide_&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;.html&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; i &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; range&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;pdf &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; render_slides_to_pdf(slides, Path(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;carousel.pdf&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;verify_carousel(pdf, expected_slides&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(slides))   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# raises before any network call&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;publish_carousel(token, author_urn, pdf.read_bytes(), title&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Reach playbook&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A dropped slide or a botched aspect ratio now fails on my machine in milliseconds, instead of going live as a malformed post I have to delete and re-run the golden hour on.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The immediate follow-up is automating “link in the first comment,” so a reach-suppressing link can never accidentally land in the body, which closes the last gap between the playbook and the published behavior. I also want to template the slide HTML so a carousel can be generated from an outline rather than hand-authored per post.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/blog/engineering/feed/understanding-feed-dwell-time&quot;&gt;LinkedIn Engineering: Understanding dwell time to improve feed ranking&lt;/a&gt; — LinkedIn ranks on dwell time because it’s measurable on every view, real-valued, and resists click-bounce.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/top-content/marketing/linkedin-content-and-ads/do-links-lower-linkedin-post-reach/&quot;&gt;LinkedIn: Do links lower post reach&lt;/a&gt; — external links in the body reduce reach; put them in the first comment.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://playwright.dev/docs/api/class-page&quot;&gt;Playwright: page.pdf() and screenshots&lt;/a&gt; — Chromium PDF generation uses print CSS media and needs print_background for graphics; screenshots use screen media.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust&quot;&gt;MDN: -webkit-print-color-adjust&lt;/a&gt; — force original colors to render instead of being adjusted for print.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/documents-api&quot;&gt;LinkedIn: Documents API&lt;/a&gt; — three-step initialize-upload, PUT bytes, attach URN flow for PDF document posts, including the mandatory version header.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[ghostwriter] feat(ghostwriter): reach optimization, flow diagrams &amp;amp; carousels (v0.3.0) (&lt;a href=&quot;https://github.com/natejswenson/claude-skills/commit/1cf7e86b16e9bcbc400b8dc81166f7892e45d83d&quot;&gt;1cf7e86&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>An attribution mark the common crops can&apos;t remove: mark every region a reshare keeps</title><link>https://natejswenson.com/devlog/ghostwriter/v0.1.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/ghostwriter/v0.1.0/</guid><description>The LinkedIn ghostwriter got new card templates, but the real lesson was attribution: a corner byline crops off and a tiled watermark looks awful, so the fix was to put a mark on every region a reshare keeps, bracketing the headline with the signature and co-locating the handle with the figure, so a bottom-strip, top-strip, or single-side crop can&apos;t take a region without leaving a mark. Here is the full build from card template to rendered PNG.</description><pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;The LinkedIn ghostwriter renders posts as cards, and this release added a new card type. The &lt;code&gt;ramp&lt;/code&gt; card draws three ascending steps with the final one highlighted, for posts about an accelerating progression like a growth curve, an adoption trend, or a compounding streak. It had been stranded on an unmerged branch from before the skills were consolidated, so bringing it into the monorepo meant a PR with the scorer and full test suite green. I also ran the skill end to end for the first time, drafting and publishing a real post with the new card.&lt;/p&gt;
&lt;p&gt;That live run is where the interesting problem showed up, and it had nothing to do with the cards themselves. It was attribution: when a designed card gets reshared, how do you keep your name on it? The part worth writing about is how a signature survives a crop, and the rest of this post is the end-to-end build, from the card template through the rendered PNG to a check that the mark actually holds.&lt;/p&gt;
&lt;h2&gt;Setup: the card template&lt;/h2&gt;
&lt;p&gt;A card is just an HTML document the renderer will later turn into an image. Before the attribution work, the structure needs three regions: an eyebrow row at the top, a headline, and a body that holds the figure or copy. Keeping these as named regions matters, because the attribution fix is going to change the eyebrow from a single label into a two-sided row, and everything downstream should keep working when it does.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;&amp;#x3C;!-- card.html — the base template, one fixed-size canvas the renderer screenshots. --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;!&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;doctype&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; html&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; lang&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; charset&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    :root&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { --ink&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #14171a&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; --muted&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #5b6770&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; --accent&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #2f6df0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    *&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { box-sizing&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; border-box&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    body&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { margin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; font-family&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; Inter, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;system-ui&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;sans-serif&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--ink&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .card&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { position&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; relative&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; width&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1200px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; height&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1200px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; padding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 72px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; flex-direction&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; column&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .card-eyebrow&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* filled in by the signature step */&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .card-headline&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { font-size&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 64px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; line-height&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1.1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; margin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 24px&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .card-body&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { flex&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;header&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-eyebrow&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;&amp;#x3C;!-- headline + signature go here --&gt;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;header&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-body&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;&amp;#x3C;!-- ramp figure or copy goes here --&gt;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fixed &lt;code&gt;1200px&lt;/code&gt; square is the LinkedIn-friendly canvas the renderer captures. Everything from here builds inside the eyebrow and the body.&lt;/p&gt;
&lt;h2&gt;Build the signature into the headline row&lt;/h2&gt;
&lt;p&gt;The obvious approaches both fail. A byline in the bottom corner is trivial to crop off; anyone can screenshot the card minus the last strip and the attribution is gone. A tiled watermark across the whole card survives cropping, but it looks terrible over a real design and undercuts the thing it’s supposed to protect.&lt;/p&gt;
&lt;p&gt;The fix was to stop treating the signature as a decoration that sits in spare space and start treating it as part of the content a reshare keeps. A card has two regions worth keeping, the headline and the figure, so I marked both. I put a copy of the avatar and handle at each end of the headline row and let the headline fill the space between them, so a mark sits at both horizontal extremes of the line nobody wants to lose; and I dropped the handle into the figure’s own note, so the chart that gets reshared carries its attribution with it. Now the lazy crops each leave a mark: a bottom-strip crop keeps the top row with both bracketing marks, a top-strip crop that drops the eyebrow still keeps the figure and its handle, and either single-side crop of the headline leaves a mark on the far end. Be clear about the limit, though: no pure-CSS layout resists an arbitrary rectangular crop, because a determined editor can always cut a tight window around one region and drop every mark around it. What this defeats is the lazy reshare, the single straight strip people actually make, by putting a mark on each region a reshare needs to keep.&lt;/p&gt;
&lt;p&gt;This matters because resharing is exactly when provenance gets destroyed. The metadata-based answer to “who made this” is the cryptographic kind, like C2PA Content Credentials, but the C2PA spec itself is candid that it “does not offer any protection against the complete removal of C2PA manifests,” and names the obvious attack: download a credentialed image from a social site, strip the manifest, and re-post it (&lt;a href=&quot;https://spec.c2pa.org/specifications/specifications/2.4/security/Security_Considerations.html&quot;&gt;C2PA: Security Considerations&lt;/a&gt;). Platforms recompress and re-encode uploads as a matter of course, so the content most likely to go viral is the content most likely to arrive stripped of its provenance, which is why C2PA leans on manifest repositories and soft bindings to recover what embedding alone can’t guarantee (&lt;a href=&quot;https://worldprivacyforum.org/posts/privacy-identity-and-trust-in-c2pa/&quot;&gt;World Privacy Forum: Privacy, Identity and Trust in C2PA&lt;/a&gt;). A visible, crop-resistant mark is the layperson’s version of the same goal: provenance that travels with the pixels instead of with metadata a re-upload will quietly drop.&lt;/p&gt;
&lt;p&gt;The technique is small. Lay the eyebrow out as a flex row, give the headline &lt;code&gt;flex: 1&lt;/code&gt; so it fills the middle, and place a signature on each side.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;&amp;#x3C;!-- Goes inside .card-eyebrow. A signature sits at each end of the headline&apos;s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;     row, so a bottom-strip crop and either single-side crop all keep a mark. --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-sig&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;https://www.linkedin.com/in/yourhandle&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-sig__avatar&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;avatar.jpg&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; alt&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-sig__handle&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;@yourhandle&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-headline&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Three releases, one compounding streak&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-sig&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;https://www.linkedin.com/in/yourhandle&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-sig__handle&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;@yourhandle&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card-eyebrow&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  gap&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 24px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card-headline&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { flex&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; margin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* headline fills the space between the two marks */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card-sig&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; gap&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 12px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; flex-shrink&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; text-decoration&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; none&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--muted&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card-sig__avatar&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { width&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 56px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; height&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 56px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; border-radius&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 50%&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card-sig__handle&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { font-weight&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 600&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; white-space&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; nowrap&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A flex row with a flexible middle child is the whole idea: the headline stretches between two marks pinned to the ends (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout/Basic_concepts_of_flexbox&quot;&gt;MDN: Basic concepts of flexbox&lt;/a&gt;). The &lt;code&gt;flex-shrink: 0&lt;/code&gt; on each signature keeps the avatars and handles from collapsing when a long headline competes for width, so the marks stay legible at both ends instead of degrading into the corner case they were meant to avoid.&lt;/p&gt;
&lt;h2&gt;Build an honest ramp figure&lt;/h2&gt;
&lt;p&gt;The ramp card draws bars in the body, and the bars are drawn to scale: each height is proportional to the figure it represents, and the labeled figures next to them carry the exact numbers. That was a conscious call, because a bar chart whose heights don’t track the quantities is one of the most reliable ways to mislead an audience by accident.&lt;/p&gt;
&lt;p&gt;The risk is well documented. When a bar’s size doesn’t track the quantity it represents, viewers still read it as if it did, and the misreading is stubborn. Research on misleading bar magnitudes makes the point: a study of axis-truncated bar graphs found the distortion persisted even after participants were explicitly taught about the effect, showing up in 83.5% of participants across five studies (&lt;a href=&quot;https://www.sciencedirect.com/science/article/abs/pii/S2211368120300978&quot;&gt;Truncating Bar Graphs Persistently Misleads Viewers&lt;/a&gt;). The mechanism is the same whether the axis is truncated or the heights are simply not proportional: people trust the shape over the caption.&lt;/p&gt;
&lt;p&gt;The honest move is to make the heights track the numbers and then label the exact figures, so the shape and the caption tell the same story (&lt;a href=&quot;https://data.europa.eu/en/publications/datastories/honest-charts-ethics-and-integrity-data-visualisation&quot;&gt;data.europa.eu: Honest charts&lt;/a&gt;). The bar heights here are proportional to the figures: 1, 4, and 9 posts render as 11%, 44%, and 100% of the tallest bar, set as fixed inline heights, and the same figures appear in the labels next to each step. Anyone who reads the numbers gets the truth, and the shape they trust at a glance agrees with it. The attribution note is on the card itself rather than buried in a caption nobody screenshots, and it carries the handle, so a crop that keeps the chart and drops the eyebrow keeps a mark.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;&amp;#x3C;!-- Goes inside .card-body. Heights are proportional to the figures (1/4/9 -&gt; 11/44/100%); labels carry the exact numbers. --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;figure&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; aria-label&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Ascending progression, bars proportional to the figures&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__bar&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height: 11%&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__label&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;v0.1 · 1 post&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__bar&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height: 44%&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__label&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;v0.2 · 4 posts&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__bar ramp__bar--hero&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height: 100%&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__label&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;v0.3 · 9 posts&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;figcaption&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__note&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__sig&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;@yourhandle&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; · Bars proportional to the figures.&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;figcaption&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;figure&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.ramp&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex-end&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; gap&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 32px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; width&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 100%&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; height&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 100%&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; margin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.ramp__bar&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { flex&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; background&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #d7dee8&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; border-radius&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 16px&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 16px&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex-end&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; justify-content&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.ramp__bar--hero&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { background&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--accent&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }     &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* highlight the final step */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.ramp__label&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { padding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 12px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; font-size&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 22px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; font-weight&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 600&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--ink&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.ramp__note&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { position&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; absolute&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; bottom&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 72px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; left&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 72px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; font-size&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 18px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--muted&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.ramp__sig&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { font-weight&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 600&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }    &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* the figure&apos;s own attribution mark */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The bars read as ascending in exactly the proportion the labels state, so the first step really is one-ninth the height of the last. The visual reads as honest because the shape and the numbers agree.&lt;/p&gt;
&lt;h2&gt;The assembled card&lt;/h2&gt;
&lt;p&gt;The base template, the bracketed signature row, and the ramp figure are one file. This is the complete &lt;code&gt;card.html&lt;/code&gt; the renderer and the test below both take as input.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;&amp;#x3C;!-- card.html — base template, signature row, and ramp figure assembled into one file. --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;!&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;doctype&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; html&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; lang&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; charset&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    :root&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { --ink&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #14171a&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; --muted&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #5b6770&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; --accent&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #2f6df0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    *&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { box-sizing&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; border-box&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    body&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { margin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; font-family&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; Inter, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;system-ui&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;sans-serif&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--ink&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .card&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { position&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; relative&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; width&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1200px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; height&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1200px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; padding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 72px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; flex-direction&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; column&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .card-eyebrow&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; gap&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 24px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .card-headline&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { flex&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; margin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; font-size&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 64px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; line-height&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1.1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .card-sig&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; gap&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 12px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; flex-shrink&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; text-decoration&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; none&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--muted&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .card-sig__avatar&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { width&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 56px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; height&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 56px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; border-radius&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 50%&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .card-sig__handle&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { font-weight&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 600&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; white-space&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; nowrap&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .card-body&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { flex&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .ramp&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex-end&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; gap&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 32px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; width&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 100%&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; height&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 100%&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; margin&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .ramp__bar&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { flex&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; background&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #d7dee8&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; border-radius&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 16px&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 16px&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; align-items&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; flex-end&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; justify-content&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .ramp__bar--hero&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { background&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--accent&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .ramp__label&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { padding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 12px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; font-size&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 22px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; font-weight&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 600&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--ink&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .ramp__note&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { position&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; absolute&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; bottom&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 72px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; left&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 72px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; font-size&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 18px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--muted&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    .ramp__sig&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { font-weight&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 600&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;head&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;header&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-eyebrow&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-sig&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;https://www.linkedin.com/in/yourhandle&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-sig__avatar&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;avatar.jpg&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; alt&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-sig__handle&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;@yourhandle&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-headline&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;Three releases, one compounding streak&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-sig&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;https://www.linkedin.com/in/yourhandle&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-sig__handle&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;@yourhandle&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;header&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card-body&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;figure&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; aria-label&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Ascending progression, bars proportional to the figures&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__bar&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height: 11%&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__label&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;v0.1 · 1 post&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__bar&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height: 44%&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__label&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;v0.2 · 4 posts&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__bar ramp__bar--hero&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height: 100%&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__label&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;v0.3 · 9 posts&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;figcaption&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__note&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ramp__sig&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;@yourhandle&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;span&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; · Bars proportional to the figures.&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;figcaption&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;figure&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;.card&lt;/code&gt; carries &lt;code&gt;position: relative&lt;/code&gt; so the absolutely positioned &lt;code&gt;.ramp__note&lt;/code&gt; anchors to the card rather than the viewport. One coupling to know about: the note’s &lt;code&gt;bottom: 72px&lt;/code&gt; is deliberately the card’s &lt;code&gt;72px&lt;/code&gt; padding, which is what lands the mark inside the body’s vertical band where the crop test checks for it. If you change the card padding without changing this offset, the mark drifts out of that band and the crop test below correctly fails; keep the two in sync, or anchor the note inside the figure’s own flow instead. Swap the &lt;code&gt;@yourhandle&lt;/code&gt; strings, the &lt;code&gt;href&lt;/code&gt;, and &lt;code&gt;avatar.jpg&lt;/code&gt; for your own.&lt;/p&gt;
&lt;h2&gt;Use it: render the card to a PNG&lt;/h2&gt;
&lt;p&gt;The card is HTML, and a post needs an image to attach. A headless browser renders the markup exactly as a browser would and screenshots it, which keeps the flex layout and fonts faithful rather than re-implementing them in an image library. Playwright’s &lt;code&gt;screenshot&lt;/code&gt; captures a page or a single element to a PNG (&lt;a href=&quot;https://playwright.dev/python/docs/screenshots&quot;&gt;Playwright: Screenshots&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Install Playwright and its browser once:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;pip&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; playwright&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;playwright&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; chromium&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# render.py — turn a card HTML file into a PNG ready to attach to a post.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; playwright.sync_api &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; render_card&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(html_path: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, out_path: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, size: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1200&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        browser &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p.chromium.launch()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # Match the .card canvas so the screenshot has no scrollbars or letterboxing.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; browser.new_page(viewport&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: size, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: size},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                                device_scale_factor&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 2x for crisp text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page.goto(Path(html_path).resolve().as_uri())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page.wait_for_load_state(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;networkidle&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)          &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# let the avatar image settle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page.locator(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.card&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).screenshot(path&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;out_path)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# screenshot the card element only&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        browser.close()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; out_path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __name__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    render_card(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card.html&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;post.png&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Screenshotting the &lt;code&gt;.card&lt;/code&gt; locator rather than the whole page means the output is exactly the square canvas, with the marks bracketing the headline as laid out. With &lt;code&gt;device_scale_factor=2&lt;/code&gt; the file lands at 2400px on a side, twice the 1200px CSS canvas, so the text stays crisp after LinkedIn recompresses it; drop the factor to 1 if you want a 1200px file instead. That PNG is what gets uploaded with the post.&lt;/p&gt;
&lt;h2&gt;Verify the mark survives a crop&lt;/h2&gt;
&lt;p&gt;The claim is that the straight-strip crops people actually make can’t take a region without taking a mark, and that is testable. Three facts should hold. The two headline marks each sit inside the headline’s vertical band, so a top- or bottom-strip crop that keeps the headline keeps them. One headline mark sits on either side of the headline, so a single left- or right-edge crop that keeps the headline still leaves a mark on the far end. And the figure carries its own mark inside the body’s band, so a top-strip crop that drops the eyebrow but keeps the chart still keeps attribution. Read the bounding boxes from the same render and assert all three, so the layout can’t silently drift back into a single croppable corner.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# test_signature.py — fail if a single straight strip could take a region without a mark.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; playwright.sync_api &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;TOL &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 4&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  # px of slack for sub-pixel rounding and vertical centering&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; contains_vertically&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(outer: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, inner: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dict&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, tol: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; TOL) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # inner&apos;s whole vertical band sits within outer&apos;s, so any strip that keeps&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # outer also keeps inner (overlap &gt; 0 isn&apos;t enough: a centered mark in a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # tall row can still be sheared off by a strip that keeps one edge).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (inner[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;y&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; outer[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;y&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tol&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            and&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; inner[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;y&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; inner[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; outer[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;y&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; outer[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tol)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_every_region_keeps_a_mark&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    url &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;card.html&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).resolve().as_uri()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        browser &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p.chromium.launch()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; browser.new_page(viewport&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1200&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1200&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page.goto(url)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page.wait_for_load_state(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;load&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# let layout and the avatar settle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        head &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; page.locator(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.card-headline&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).bounding_box()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        body &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; page.locator(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.card-body&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).bounding_box()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        marks &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [m.bounding_box() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; m &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; page.locator(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.card-sig&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).all()]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        fig_mark &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; page.locator(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.ramp__sig&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).bounding_box()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        browser.close()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(marks) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Each headline mark is contained in the headline&apos;s band -&gt; a top- or&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # bottom-strip crop that keeps the headline can&apos;t shear a mark off it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; all&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(contains_vertically(head, m) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; m &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; marks)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # A mark left of the headline -&gt; survives a right-edge crop that keeps the headline.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(m[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; head[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; m &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; marks)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # A mark right of the headline -&gt; survives a left-edge crop that keeps the headline.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; any&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(m[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; m[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; head[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;x&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; head[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; m &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; marks)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # The figure carries its own mark inside the body band -&gt; a top-strip crop&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # that drops the eyebrow but keeps the chart still keeps attribution.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; contains_vertically(body, fig_mark)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If someone moves a signature out of the headline row, its band stops being contained in the headline’s and the test fails; if someone drops the second copy, the bracketing assertions fail; if the figure loses its handle, the body-band assertion fails. The structural guarantee, that a mark rides on every region a reshare keeps, becomes a regression test instead of a note in a design doc.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;I want more reps of the skill running end to end on real posts, since the first live run is what surfaced both the signature approach and a round of fluff-word trimming. The card library grows from what real posts actually need, so the next templates will come from drafts rather than from guessing at categories up front.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://spec.c2pa.org/specifications/specifications/2.4/security/Security_Considerations.html&quot;&gt;C2PA: Security Considerations&lt;/a&gt; — the spec admits manifests can be fully removed by re-uploading and re-posting, motivating other bindings.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://worldprivacyforum.org/posts/privacy-identity-and-trust-in-c2pa/&quot;&gt;World Privacy Forum: Privacy, Identity and Trust in C2PA&lt;/a&gt; — technical review of content provenance and why metadata-based attribution is fragile in distribution.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.sciencedirect.com/science/article/abs/pii/S2211368120300978&quot;&gt;Truncating Bar Graphs Persistently Misleads Viewers&lt;/a&gt; — viewers misread non-proportional bars even after being taught about the effect.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://data.europa.eu/en/publications/datastories/honest-charts-ethics-and-integrity-data-visualisation&quot;&gt;data.europa.eu: Honest charts&lt;/a&gt; — accurate data isn’t enough; labels and annotations are the safeguard against misleading visuals.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout/Basic_concepts_of_flexbox&quot;&gt;MDN: Basic concepts of flexbox&lt;/a&gt; — a flex row with a &lt;code&gt;flex: 1&lt;/code&gt; middle child holds the headline between two marks pinned to the ends.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://playwright.dev/python/docs/screenshots&quot;&gt;Playwright: Screenshots&lt;/a&gt; — capturing a page or a single element to a PNG with a headless browser.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat(ghostwriter): add ramp card type for accelerating progressions (#1) (&lt;a href=&quot;https://github.com/natejswenson/claude-skills/commit/c2c6b2be4e1a698d266c52940141cdf5561d28b6&quot;&gt;c2c6b2b&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Scoping a monorepo&apos;s history is a view, not a partition</title><link>https://natejswenson.com/devlog/devlog/v0.2.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/devlog/v0.2.0/</guid><description>After folding three skills into one repo, devlog couldn&apos;t tell which skill a commit belonged to. The fix was a pathspec-scoped git log, which raised two things worth teaching: a path filter is a view over shared history, and a path filter is also untrusted input headed for git.</description><pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;I folded three separate skills into a single repo, and the dev-log generator immediately got confused: it gathered every commit in the repo and had no way to tell which skill a given commit belonged to. v0.2.0 adds an optional &lt;code&gt;pathFilter&lt;/code&gt; config field that scopes a project’s commit gathering to a repo-relative subdirectory, like &lt;code&gt;skills/devlog&lt;/code&gt;. Several logical projects can now share the same &lt;code&gt;path&lt;/code&gt; and &lt;code&gt;remote&lt;/code&gt; while each collects only its own subtree’s commits; under the hood, the &lt;code&gt;git log&lt;/code&gt; call grows a &lt;code&gt;-- &amp;lt;pathFilter&amp;gt;&lt;/code&gt; pathspec. Commit links still resolve to the shared repo, so nothing about navigation changes. The value is validated like every other path that reaches git (no leading dash or slash, no &lt;code&gt;..&lt;/code&gt;), surfaced as a &lt;code&gt;scope:&lt;/code&gt; line in &lt;code&gt;devlog config&lt;/code&gt;, and documented in &lt;a href=&quot;http://SKILL.md&quot;&gt;SKILL.md&lt;/a&gt; with a security-validation row.&lt;/p&gt;
&lt;p&gt;The feature is small. The two ideas underneath it are not: scoping history to a subdirectory is a view, not a partition, and a path you hand to git is input you have to distrust. Here is the end-to-end build, from the config field to the test that keeps the guard honest.&lt;/p&gt;
&lt;h2&gt;Setup: several projects, one repo&lt;/h2&gt;
&lt;p&gt;The config used to assume one project per repo. The change is one optional field. A project keeps its &lt;code&gt;path&lt;/code&gt; (the local checkout) and &lt;code&gt;remote&lt;/code&gt; (where commit links point), and may add a &lt;code&gt;pathFilter&lt;/code&gt; naming the subtree it owns. Two projects pointing at the same &lt;code&gt;path&lt;/code&gt; and &lt;code&gt;remote&lt;/code&gt; with different &lt;code&gt;pathFilter&lt;/code&gt; values are now two distinct feeds over one repository.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// devlog.config.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// Several logical projects can share one checkout. `pathFilter` scopes a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// project&apos;s commit feed to a subdirectory of that repo. Omit it for the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// original behavior: the whole repository.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  projects: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      key: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;devlog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      path: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;/Users/me/src/claude-skills&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      remote: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;https://github.com/natejswenson/claude-skills&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      pathFilter: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;skills/devlog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,        &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// scope to this subtree&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      key: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ghostwriter&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      path: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;/Users/me/src/claude-skills&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,     &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// same checkout&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      remote: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;https://github.com/natejswenson/claude-skills&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      pathFilter: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;skills/ghostwriter&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      key: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;site&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      path: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;/Users/me/src/natejswenson.io&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      remote: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;https://github.com/natejswenson/natejswenson.io&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;      // no pathFilter: whole repo, the original one-project-per-repo case&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A pathspec turns one repo into several logical projects&lt;/h2&gt;
&lt;p&gt;Putting multiple projects in one repository is the monorepo pattern, and it’s how some of the largest codebases in the world are organized; Google, Meta, and Microsoft all run very large monorepos, with Microsoft moving Windows to a Git monorepo (&lt;a href=&quot;https://en.wikipedia.org/wiki/Monorepo&quot;&gt;Monorepo, Wikipedia&lt;/a&gt;). The appeal is shared tooling and atomic cross-project changes, and it pairs naturally with trunk-based development, where everyone integrates into one trunk that stays releasable (&lt;a href=&quot;https://trunkbaseddevelopment.com/monorepos/&quot;&gt;Trunk Based Development: Monorepos&lt;/a&gt;). The cost is that “the project” is no longer “the repo.” A tool that assumed one project per repo, like mine did, now over-collects.&lt;/p&gt;
&lt;p&gt;Git already has the primitive for slicing history by location: the pathspec. &lt;code&gt;git log -- &amp;lt;path&amp;gt;&lt;/code&gt; shows only the commits that touched files under that path, by filtering history down to the commits needed to explain how those files came to be (&lt;a href=&quot;https://git-scm.com/docs/git-log&quot;&gt;git-log docs&lt;/a&gt;). So a per-project commit feed is just a &lt;code&gt;git log&lt;/code&gt; with a pathspec for that project’s subtree. There is no restructuring, no submodules, and no rewriting history; one project sets &lt;code&gt;pathFilter: skills/devlog&lt;/code&gt;, another sets &lt;code&gt;skills/ghostwriter&lt;/code&gt;, and each feed collects its own subtree from the same shared repo.&lt;/p&gt;
&lt;h2&gt;Build: validate the path before git ever sees it&lt;/h2&gt;
&lt;p&gt;Here’s the trap. That &lt;code&gt;pathFilter&lt;/code&gt; value comes from config, gets handed to a command, and reaches git. The intuition is “it’s my own config, it’s fine.” That intuition is exactly how argument injection happens. Even setting aside classic shell metacharacters, an argument that starts with a dash is the dangerous case: git reads it as an option, not a path. A value passed where a path was expected stops being data and becomes a flag, and this exact class of bug has produced real CVEs in git-adjacent tooling, including argument injection through dash-leading values in VCS wrappers (&lt;a href=&quot;https://snyk.io/blog/argument-injection-when-using-git-and-mercurial/&quot;&gt;Snyk: argument injection in git and mercurial&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The first defense is to validate against an allowlist of what the value is allowed to be, rather than trying to block known-bad patterns, which is the approach OWASP recommends because denylists are easy to evade (&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html&quot;&gt;OWASP: Input Validation Cheat Sheet&lt;/a&gt;). For a pathspec that means: allow only safe characters, reject a leading dash, reject a leading slash, and reject &lt;code&gt;..&lt;/code&gt; traversal.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// validatePathFilter.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// Allowlist what a pathspec is allowed to be. Don&apos;t denylist known-bad&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// patterns; allowlists are the OWASP-recommended default because denylists&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// are easy to evade.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SAFE_PATHSPEC &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /^[A-Za-z0-9._/-]+$/&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; validatePathFilter&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(value) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (value &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; null) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; null;            &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// no filter: gather the whole repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;typeof&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; value &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;!==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;string&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;SAFE_PATHSPEC.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(value)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    throw&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`unsafe pathFilter (illegal characters): ${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (value.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;startsWith&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;-&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)) {               &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// git would read this as an OPTION&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    throw&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`unsafe pathFilter (leading dash): ${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (value.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;startsWith&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)) {               &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// absolute path escapes the subtree&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    throw&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`unsafe pathFilter (leading slash): ${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (value.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;includes&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;..&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)) {                &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// parent traversal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    throw&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`unsafe pathFilter (traversal): ${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; value;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { validatePathFilter };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second defense is git’s &lt;code&gt;--&lt;/code&gt; separator, the POSIX convention that marks the end of options, so everything after it is treated as a path even if it looks like a flag (&lt;a href=&quot;https://git-scm.com/docs/git-log&quot;&gt;git-log docs&lt;/a&gt;). It’s worth seeing why this matters at the command line. Without the terminator, a dash-leading value is parsed as an option and silently changes what git does:&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Suppose a project&apos;s pathFilter were the string &quot;--all&quot;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Passed where a path was expected, git reads it as an OPTION and ignores&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# your scoping intent, listing every ref&apos;s history instead of one subtree:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; log&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --format=&apos;%H %s&apos;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --all&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# The `--` terminator ends option parsing. Everything after it is a path,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# even if it starts with a dash, so the value can only ever be data:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; log&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --format=&apos;%H %s&apos;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;skills/devlog&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Either defense alone is a meaningful improvement; together they close both the “looks like a flag” and the “contains something nasty” doors.&lt;/p&gt;
&lt;h2&gt;Use it: gather one project’s commits&lt;/h2&gt;
&lt;p&gt;Now wire the two defenses into the gather step. Validation runs first, so a bad value never reaches git at all. Then git is invoked with its arguments as an array, which means there is no shell to reinterpret them (&lt;a href=&quot;https://nodejs.org/api/child_process.html&quot;&gt;Node.js child_process&lt;/a&gt;), and the validated pathspec goes after &lt;code&gt;--&lt;/code&gt;. The commit URL is built from &lt;code&gt;project.remote&lt;/code&gt;, so links resolve to the shared repo no matter how the feed is scoped.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// gather.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { execFileSync } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;node:child_process&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { validatePathFilter } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;./validatePathFilter&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// Gather one project&apos;s commits. Args-as-array means no shell parses them,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// and `--` guarantees the pathspec is read as a path. Validation runs first&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// so an unsafe value is refused before git is ever spawned.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; gatherCommits&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(project) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathFilter &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; validatePathFilter&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(project.pathFilter);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; args &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;-C&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, project.path, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;log&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;--format=%H%x00%s&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (pathFilter) args.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;push&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;--&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, pathFilter);   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// terminator, then the path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // maxBuffer defaults to 1 MB; a real monorepo&apos;s full git log can exceed&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // that and throw ENOBUFS, so raise it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; out &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; execFileSync&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, args, { encoding: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, maxBuffer: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1024&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1024&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 64&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; out&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(Boolean)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;((line) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [sha, subject] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; line.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\x00&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;      // Link points at the shared repo; scoping changes which commits we&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;      // collect, never where they live.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { sha, subject, url: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;project&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;remote&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}/commit/${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;sha&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { gatherCommits };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;gatherCommits&lt;/code&gt; for the &lt;code&gt;devlog&lt;/code&gt; project and you get only the commits that touched &lt;code&gt;skills/devlog&lt;/code&gt;, each linking back to the shared &lt;code&gt;claude-skills&lt;/code&gt; repo. Run it for &lt;code&gt;ghostwriter&lt;/code&gt; against the same checkout and you get a disjoint-by-intent feed from the same history. The &lt;code&gt;site&lt;/code&gt; project, with no &lt;code&gt;pathFilter&lt;/code&gt;, behaves exactly as it did before.&lt;/p&gt;
&lt;h2&gt;Verify it: prove the guard holds&lt;/h2&gt;
&lt;p&gt;The two failure modes worth defending are testable: a malicious &lt;code&gt;pathFilter&lt;/code&gt; must be refused, and a normal one must pass through and scope. These became regression tests so the validation can’t quietly rot.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// validatePathFilter.test.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; test &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;node:test&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; assert &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;node:assert/strict&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { validatePathFilter } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;./validatePathFilter&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;refuses an option-injecting pathFilter&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  assert.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;throws&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; validatePathFilter&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;--all&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;),&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /leading dash/&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;refuses parent traversal&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  assert.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;throws&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; validatePathFilter&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;../../etc&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;),&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /traversal/&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;refuses an absolute path&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  assert.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;throws&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; validatePathFilter&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;/etc/passwd&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;),&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /leading slash/&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;accepts a normal subtree and returns it unchanged&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  assert.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;equal&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;validatePathFilter&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;skills/devlog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;), &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;skills/devlog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;treats a missing filter as whole-repo&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  assert.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;equal&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;validatePathFilter&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(null), null);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first three tests fail the moment their guard is removed; &lt;code&gt;--all&lt;/code&gt; and &lt;code&gt;../../etc&lt;/code&gt; are exactly the shapes that turn a path into a flag or a traversal. The last two prove the common cases still work: a real subtree passes through verbatim, and an absent filter falls back to the original whole-repo behavior.&lt;/p&gt;
&lt;h2&gt;Scoping is a view, not a partition&lt;/h2&gt;
&lt;p&gt;The honest consequence of building this on &lt;code&gt;git log -- &amp;lt;path&amp;gt;&lt;/code&gt; is that the filter is a lens over one shared history, not a wall between separate histories. A single commit that spans two skills will show up in both feeds. I could have deduplicated, or forced each commit to belong to exactly one project, but that would be lying about what the commit did; it really did touch both subtrees. The pathspec answers “which commits touched this path,” and a commit that touched two paths is correctly two answers. Letting it appear twice keeps the feed faithful to the repository instead of faithful to a tidy mental model. It’s the same reason &lt;code&gt;git log -- src/ docs/&lt;/code&gt; happily lists a commit that changed both.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;I want to watch the first real multi-skill run and confirm entries land in the right feeds before adding anything. After that, the obvious follow-up is letting &lt;code&gt;init&lt;/code&gt; and &lt;code&gt;add-project&lt;/code&gt; prompt for &lt;code&gt;pathFilter&lt;/code&gt; interactively, so the monorepo case is a guided choice instead of a config field you have to know exists.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-log&quot;&gt;git-log documentation&lt;/a&gt; — &lt;code&gt;git log -- &amp;lt;path&amp;gt;&lt;/code&gt; limits history to a pathspec; &lt;code&gt;--&lt;/code&gt; separates paths from options and revisions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Monorepo&quot;&gt;Monorepo (Wikipedia)&lt;/a&gt; — multiple projects in one repository; the scale at which Google, Meta, and Microsoft run it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://trunkbaseddevelopment.com/monorepos/&quot;&gt;Trunk Based Development: Monorepos&lt;/a&gt; — why the monorepo pattern pairs with a single releasable trunk.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html&quot;&gt;OWASP: Input Validation Cheat Sheet&lt;/a&gt; — allowlist what’s valid; denylists are evadable.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://snyk.io/blog/argument-injection-when-using-git-and-mercurial/&quot;&gt;Snyk: argument injection when using git and mercurial&lt;/a&gt; — dash-leading values read as options instead of data, a real injection class in VCS tooling.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nodejs.org/api/child_process.html&quot;&gt;Node.js child_process&lt;/a&gt; — &lt;code&gt;execFile&lt;/code&gt; runs a binary with an argument vector and no shell, so arguments can’t be reinterpreted by a shell.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;feat(devlog): subdirectory filtering for monorepo projects (0.2.0) (&lt;a href=&quot;https://github.com/natejswenson/claude-skills/commit/e970619887c0f605e08b48ceebfe41eed51b5b87&quot;&gt;e970619&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>When thinking makes the model slower and worse: turning extended reasoning off for extraction</title><link>https://natejswenson.com/devlog/resume/v0.1.1/</link><guid isPermaLink="true">https://natejswenson.com/devlog/resume/v0.1.1/</guid><description>A four-bullet résumé produced ~800 characters of JSON but billed 16,000 output tokens; the rest was extended thinking the task never needed. Turning it off, moving the self-audit into deterministic code, and letting an eval sweep settle it made a real run about 13x faster.</description><pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;OneTap Resume started as a web app that tailors a résumé to a job posting and renders a PDF. v0.1.1 turns that whole pipeline into a self-contained Claude Code skill, &lt;code&gt;/onetapresume&lt;/code&gt;, that runs locally through the &lt;code&gt;claude&lt;/code&gt; CLI on your subscription, so there’s no API key and no per-run cost. Along the way it got live streaming progress, results as tables, a native macOS file picker, and an interactive style picker that re-renders across seven templates in about half a second. It also got cleaned up for other people to use: roughly 1,580 lines of dead code swept, an unused dependency dropped (it was pulling 92 transitive packages), an MIT license, packaging, a rewritten README, and a release workflow that tags and publishes a GitHub Release on merge to master.&lt;/p&gt;
&lt;p&gt;The headline, though, was speed. A real run took five to nine minutes and sometimes hung. The fix wasn’t a faster machine or a cleverer prompt; it was noticing that the model was doing a lot of expensive work the task never asked for. What follows is the whole arc end to end: how to find the wasted work, how to build the extraction call so it stops happening, how to replace the in-prompt self-audit with deterministic code, and how to prove the change on evidence rather than vibes.&lt;/p&gt;
&lt;h2&gt;Diagnose: 800 characters of answer, 16,000 tokens of bill&lt;/h2&gt;
&lt;p&gt;The first job is to make the waste visible. Extended thinking is billed as output tokens, so if you instrument a call and compare the size of the visible answer to the billed &lt;code&gt;output_tokens&lt;/code&gt;, a large gap is the tell. A four-bullet résumé produces about 800 characters of JSON; that is a few hundred tokens of answer, not sixteen thousand.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; anthropic &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Anthropic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;client &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Anthropic()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Haiku 4.5 uses the classic extended-thinking shape: {type: &quot;enabled&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# budget_tokens: N}. budget_tokens must be &amp;#x3C; max_tokens, and output_tokens&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# is thinking + visible text, so to be billed ~16k the budget has to allow it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;resp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; client.messages.create(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    model&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;claude-haiku-4-5&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    max_tokens&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;20000&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    thinking&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;enabled&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;budget_tokens&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;16000&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the suspect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    system&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Rewrite each résumé bullet for the target job. Return ONLY a JSON array of strings.&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    messages&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: prompt}],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;answer &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join(b.text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.content &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b.type &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;answer chars:  &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(answer)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)              &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ~800&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;output tokens: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;resp.usage.output_tokens&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ~16,000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ~800 chars of JSON is a few hundred tokens, not 16k. The gap is thinking:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tokens you pay for and wait on but never see in the answer.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That gap was the two missing minutes. The model spent them reasoning through a rule-dense prompt before emitting a tiny structured answer. Anthropic’s own guidance draws the line exactly here: thinking helps on tasks that need multi-step reasoning, math, complex coding, or multi-constraint analysis, and for everyday work like reformatting or extraction, standard responses are faster and perfectly sufficient (&lt;a href=&quot;https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking&quot;&gt;Anthropic: Extended thinking&lt;/a&gt;). Their summary is blunt: when in doubt, respond directly (&lt;a href=&quot;https://docs.anthropic.com/en/docs/build-with-claude/adaptive-thinking&quot;&gt;Anthropic: Adaptive thinking&lt;/a&gt;). That call is automatic on the 4.6-and-later adaptive models, which decide per turn whether to think; Haiku 4.5 doesn’t, so you make the same call by hand with &lt;code&gt;budget_tokens&lt;/code&gt; or &lt;code&gt;{&amp;quot;type&amp;quot;: &amp;quot;disabled&amp;quot;}&lt;/code&gt;. Tailoring a bullet into structured JSON is extraction with rules, not open-ended reasoning. The prompt was dense, so the model kept thinking, but density of instructions is not the same as difficulty of reasoning, and that gap is where the wasted time lived.&lt;/p&gt;
&lt;h2&gt;Build the extraction call with thinking off&lt;/h2&gt;
&lt;p&gt;The fix is to make “don’t think” explicit rather than hoping the model decides not to. For Haiku 4.5 that means passing &lt;code&gt;thinking={&amp;quot;type&amp;quot;: &amp;quot;disabled&amp;quot;}&lt;/code&gt; instead of leaving the default to chance, and capping &lt;code&gt;max_tokens&lt;/code&gt; to the size of the real payload so a runaway answer can’t balloon.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; anthropic &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Anthropic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;client &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Anthropic()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; _parse_json_array&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(text: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Models wrap JSON in ```json fences or a short preamble, and a truncated&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    answer won&apos;t parse at all. Strip fences, grab the first [...] span, and on&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    failure ask once for the bare array (the same repair pattern used below).&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; _load&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(s: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        s &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.sub(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&quot;^```(?:json)?\s*|\s*```$&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, s.strip())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        m &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.search(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&quot;\[.*\]&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, s, re.DOTALL)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; json.loads(m.group(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; m &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; s)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    try&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _load(text)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    except&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; json.JSONDecodeError:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        resp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; client.messages.create(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            model&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;claude-haiku-4-5&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            max_tokens&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;4096&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            thinking&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;disabled&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            system&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Return ONLY the JSON array of strings from the input. No fences, no prose.&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            messages&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: text}],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        fixed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join(b.text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.content &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b.type &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # One repair only. If the retry still isn&apos;t valid JSON, let _load raise:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # better to fail loud than hand back a half-parsed array.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _load(fixed)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; extract_bullets&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(prompt: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, bullet_count: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 26&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Structured extraction: rule-dense, but not reasoning-heavy.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    Thinking is turned OFF explicitly. Extraction with rules is not&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    open-ended reasoning, so the model should answer directly instead of&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    reasoning out loud first. We default to the fast model and reserve the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    expensive reasoning budget for steps that genuinely branch.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    resp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; client.messages.create(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        model&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;claude-haiku-4-5&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,            &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# small model is plenty for extraction&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # Cap to the payload, not 1k: a 26-bullet résumé of JSON overflows 1024&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # tokens and the answer gets truncated. Scale the cap by bullet count.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        max_tokens&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;max&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2048&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, bullet_count &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 160&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        thinking&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;disabled&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},       &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# make &quot;don&apos;t think&quot; explicit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        system&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;Rewrite each résumé bullet for the target job. &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;Return ONLY a JSON array of strings. No preamble, no explanation.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        messages&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: prompt}],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join(b.text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.content &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b.type &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _parse_json_array(text)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the simplest-thing-first move that good agent design asks for: start with the least machinery that could work, and add complexity only when a simpler version demonstrably falls short (&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt;). For this call, disabling thinking dropped one extraction from about 130 seconds to about 10. The same principle carries to the CLI path: if a phase is extraction rather than reasoning, run it on a fast model with thinking off.&lt;/p&gt;
&lt;h2&gt;Move the self-audit into deterministic code&lt;/h2&gt;
&lt;p&gt;There was a second reason the old prompt was thinking so hard. It was asked to police itself. The prompt told the model to check its own output for banned summary phrases, for scope qualifiers that weren’t in the source, and for invented “X years” durations, then fix them. That self-audit ran in thinking tokens, which means I was paying for it, waiting for it, and getting no guarantee it actually happened. A system prompt is a suggestion the model usually follows and sometimes ignores, most reliably right when it matters.&lt;/p&gt;
&lt;p&gt;So the audit moves into deterministic post-processing. The same checks run as plain code against the model’s output, with a single targeted corrective retry when a check fails. The shape here follows standard guidance for production LLM output: run validation after generation and feed any violation back as a narrow correction rather than trusting the model to have caught it (&lt;a href=&quot;https://www.arthur.ai/blog/best-practices-for-building-agents-guardrails&quot;&gt;Arthur: Best practices for agent guardrails&lt;/a&gt;). Keeping those post-generation checks deterministic is my own choice; the ones here are simple string and regex tests with no judgment call, so there’s no reason to spend a model on them. This block and the ones after it assume the &lt;code&gt;client = Anthropic()&lt;/code&gt; and the helpers from the earlier blocks are already in scope.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;BANNED &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;results-driven&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;team player&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;proven track record&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;synergy&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Scope qualifiers the model likes to invent. Allowed only if the source says so.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;SCOPE_WORDS &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;enterprise-wide&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;company-wide&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;global&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;industry-leading&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; find_violations&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(bullet: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, source: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;The checks the prompt used to run &apos;out loud&apos; in thinking tokens,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    now deterministic code that runs after generation.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    low, src &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; bullet.lower(), source.lower()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    problems: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    problems &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;banned phrase: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; BANNED &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; low]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    problems &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;unsupported scope: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;w&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; w &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SCOPE_WORDS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                 if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; w &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; low &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; w &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; src]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Reject fabricated &quot;X years&quot; durations the source can&apos;t support. Match on&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # word boundaries: a bare &quot;5 year&quot; substring lives inside &quot;15 years&quot;, so a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # fabricated &quot;5 years&quot; would sail past a naive `in` check against it. This&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # covers numeric durations only: &quot;5 years&quot;, &quot;5-year&quot;, &quot;5+ yrs&quot;. Spelled-out&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # forms (&quot;five years&quot;) are out of scope.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    dur &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; r&quot;\b(\d+)\+?[\s&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;]*(?:years?|yrs?)\b&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; n &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.findall(dur, low):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.search(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;rf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;\b&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;\+?[\s-]*(?:years?|yrs?)\b&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, src):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            problems.append(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;unsupported duration: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; years&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; problems&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; repair&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(bullet: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, problems: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], prompt: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;One targeted correction, not a fresh full run. Thinking stays off.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    resp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; client.messages.create(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        model&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;claude-haiku-4-5&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        max_tokens&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1024&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        thinking&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;disabled&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        system&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Rewrite the bullet to fix the listed problems. Return ONLY the bullet text.&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        messages&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                   f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Original task:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;prompt&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;Bullet:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;bullet&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;                   &quot;Fix these problems:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;- &quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;- &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join(problems)}],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join(b.text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.content &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b.type &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).strip()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same guardrails, none of the latency, and now they’re guarantees instead of hopes.&lt;/p&gt;
&lt;h2&gt;Wire it together: extract, validate, retry&lt;/h2&gt;
&lt;p&gt;The call site is small because each piece does one thing. Extract the bullets with thinking off, validate each against the source text, and re-prompt only the bullets that fail. One corrective retry is the budget; a bullet that still fails keeps its corrected text but gets flagged so a human can look.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; logging&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;log &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; logging.getLogger(__name__)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; tailor&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(prompt: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, source: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;extract -&gt; validate -&gt; one corrective retry per bad bullet.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    clean: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; bullet &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; extract_bullets(prompt):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        problems &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; find_violations(bullet, source)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; problems:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            bullet &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; repair(bullet, problems, prompt)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            problems &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; find_violations(bullet, source)   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# re-check the repair&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; problems:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                log.warning(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;bullet still failing after retry: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;%s&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, problems)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        clean.append(bullet)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; clean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The source text is the ground truth the validator measures against, which is why it threads all the way through; a “5 years” claim is only legitimate if those words appear in what the person actually wrote.&lt;/p&gt;
&lt;h2&gt;Verify with an eval sweep&lt;/h2&gt;
&lt;p&gt;It would have been easy to stop at “thinking off felt faster” and ship it. That’s a vibe, not a measurement, and vibes don’t catch the case where a speedup quietly costs you quality. An eval is just that measurement made repeatable: give the system an input, grade the output, and let the grade decide (&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt;). So I swept the thinking budget across 0, 4,000, and 8,000 tokens and scored each result.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; score&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(bullets: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;], source: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Replace with your real rubric. Higher is better.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    Stub: penalize each deterministic violation, so fewer violations wins.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;sum&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(find_violations(b, source)) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; bullets)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; sweep&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(prompt: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, source: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, budgets&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;4000&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;8000&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)) -&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; budget &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; budgets:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        thinking &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ({&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;disabled&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; budget &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                    else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;enabled&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;budget_tokens&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: budget})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        start &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; time.monotonic()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        resp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; client.messages.create(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            model&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;claude-haiku-4-5&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            max_tokens&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;16000&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,            &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# must exceed budget_tokens&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            thinking&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;thinking,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            system&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Rewrite each résumé bullet for the target job. &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;                    &quot;Return ONLY a JSON array of strings.&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            messages&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: prompt}],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        elapsed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; time.monotonic() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; start&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        bullets &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; _parse_json_array(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.join(b.text &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.content &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; b.type &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;        print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;budget=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;budget&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&gt;5&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  score=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;score(bullets, source)&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&gt;4&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;              f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;tokens=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;resp.usage.output_tokens&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&gt;6&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  {&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;elapsed&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:5.1f&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;s&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The scorer here is a stub on purpose, the real rubric is yours; the point is that the harness, not your gut, picks the winner. The result was clean: zero thinking was both the fastest and the highest-quality setting, and the larger budgets produced zero wins. More thinking made the output slower and worse on this task. On a real 26-bullet résumé the net came out to roughly 13x faster. Picking a configuration on evidence rather than feel is what let me turn off a marquee feature with confidence instead of nerves (&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt;). The sweep also caught something I’d never have seen by eye: the previous default, Sonnet on the uncached subscription path, timed out three times out of three, so the CLI now defaults to Haiku.&lt;/p&gt;
&lt;h2&gt;One cleanup worth calling out: don’t bill a stray key&lt;/h2&gt;
&lt;p&gt;The skill is meant to run free on your subscription. But if you happen to have &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; set in your environment, the CLI uses that key instead of your subscription, and every token gets billed to that API account. That precedence is documented behavior: when the variable is set, Claude Code does not use the subscription account at all (&lt;a href=&quot;https://support.claude.com/en/articles/12304248-manage-api-key-environment-variables-in-claude-code&quot;&gt;Claude Code: Manage API key environment variables&lt;/a&gt;). For a tool whose entire pitch is “no per-run cost,” silently charging a stray key is the opposite of what it promises, so the skill guards the path explicitly rather than inheriting whatever happens to be in the environment.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# The skill runs free on your subscription. A stray ANTHROPIC_API_KEY would&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# silently override that and bill the API account, so unset it before the call.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;$ANTHROPIC_API_KEY&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ] &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;unset&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; ANTHROPIC_API_KEY&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The plan is to push tailoring under a minute by trimming the per-bullet before/after payload, which is currently heavier than it needs to be, and to add an eval-gated quality check so a regression in output quality fails the build the same way a broken test would.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking&quot;&gt;Anthropic: Extended thinking&lt;/a&gt; — use thinking for multi-step reasoning; for extraction and reformatting, respond directly.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/build-with-claude/adaptive-thinking&quot;&gt;Anthropic: Adaptive thinking&lt;/a&gt; — “when in doubt, respond directly”; the 4.6+ adaptive models make this call per turn, where Haiku 4.5 needs you to set it by hand.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/research/building-effective-agents&quot;&gt;Anthropic: Building effective agents&lt;/a&gt; — start simple, measure, add complexity only when a simpler version falls short.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt; — an eval gives an input, grades the output, and lets the grade decide.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.arthur.ai/blog/best-practices-for-building-agents-guardrails&quot;&gt;Arthur: Best practices for agent guardrails&lt;/a&gt; — validate after generation and re-prompt on violations instead of trusting self-checks (keeping those checks deterministic is my own design choice).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.claude.com/en/articles/12304248-manage-api-key-environment-variables-in-claude-code&quot;&gt;Claude Code: Manage API key environment variables&lt;/a&gt; — a set &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; overrides the subscription and bills the API account.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[onetapskill] feat: streaming UX, thinking-off speedup, style picker; lean dead-code sweep (&lt;a href=&quot;https://github.com/natejswenson/onetapskill/commit/48b7f9a8f8cd646fa693212e7af64ab49e7f5f9a&quot;&gt;48b7f9a&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[onetapskill] ci: auto-tag semver release on merge to master (&lt;a href=&quot;https://github.com/natejswenson/onetapskill/commit/a75b318f36a83e339ab32c81d6191580886d7224&quot;&gt;a75b318&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[onetapskill] docs: license, packaging metadata, README for public consumption (&lt;a href=&quot;https://github.com/natejswenson/onetapskill/commit/e3643fe12274b9be44166e145deb1f2db544698f&quot;&gt;e3643fe&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[onetapskill] release: 0.1.1 — license + packaging for public consumption (&lt;a href=&quot;https://github.com/natejswenson/onetapskill/commit/03f247470960d34ba687ef91b77f7f3625e63db3&quot;&gt;03f2474&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>A prompt is code you can&apos;t compile, so score it against the schema it feeds</title><link>https://natejswenson.com/devlog/local-fitness/v0.1.0/</link><guid isPermaLink="true">https://natejswenson.com/devlog/local-fitness/v0.1.0/</guid><description>I turned a personal Garmin-coaching script into real software: a scored prompt, a hermetic test core, and version-driven releases. The reusable idea is treating the agent&apos;s prompt as a testable artifact and cross-validating it against the Pydantic schema its output is checked against, so drift is caught in CI instead of crashing a user&apos;s briefing in production. Here is the full build.</description><pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;local-fitness pulls my Garmin data into a local SQLite database and lets a Claude agent write a daily training briefing. Until v0.1.0 it was a personal script; this release treats it like software someone else could run. I added a pytest suite over the deterministic core (database helpers, the user-notes store, the schemas, the baselines, the agent’s query tools) running against a seeded temporary database with no network, put it behind a package coverage gate, and wired CI to run linting, tests, and a prompt scorer on every push. A version-driven release workflow cuts a GitHub Release whenever the version in the project file is new, and I rewrote the README from private notes into a shareable OSS project with a license, badges, a privacy section, and cross-platform setup. The first CI run earned its keep by catching a real bug.&lt;/p&gt;
&lt;p&gt;The part worth writing about is what it means to test an agent at all. Most of the app is ordinary code you can assert against, but the briefing is produced by a prompt, and a prompt doesn’t compile. So I scored it, and the check I’m happiest with cross-validates the prompt against the output schema it feeds. Here is the end-to-end build: the schema, the scorer, the hermetic test core, the CI wiring, and the failures that prove it works.&lt;/p&gt;
&lt;h2&gt;A prompt is code you can’t compile&lt;/h2&gt;
&lt;p&gt;The prompt that tells the agent how to write a briefing is logic, the same way a function is logic. It carries rules (never fabricate a number), translations (turn training-load jargon into plain coaching language), tone, and the user’s saved notes. You can’t run a type checker over English, so the usual safety net is missing exactly where the behavior is most load-bearing. The answer isn’t to give up on rigor; it’s to grade the prompt against grounded pass/fail checks, the same discipline you’d apply to any other untested surface.&lt;/p&gt;
&lt;p&gt;This is what Anthropic calls evals, and their framing maps cleanly onto a coaching agent: groundedness checks that claims trace back to real data, coverage checks for the facts a good answer must include, and graders that turn a fuzzy “is this good” into concrete outcomes (&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt;). My scorer is small and opinionated. It confirms the prompt keeps the never-fabricate rule, that it still translates the jargon, that the coaching tone survives an edit, and that saved user notes are actually wired in. None of those are unit tests in the classic sense, but each one is a real assertion about a part of the app I’d otherwise only check by reading it and hoping. The strongest of those assertions ties the prompt to something machine-readable, so start there.&lt;/p&gt;
&lt;h2&gt;Setup: make the output schema the single source of truth&lt;/h2&gt;
&lt;p&gt;The briefing’s output is validated against a set of Pydantic enums: the allowed metrics and the allowed tones, the vocabulary the rest of the app understands. Pydantic enforces that a value is a real member of the enum or it doesn’t pass (&lt;a href=&quot;https://docs.pydantic.dev/latest/api/standard_library_types/&quot;&gt;Pydantic: Standard Library Types&lt;/a&gt;). Defining these enums once gives every later step a contract to check against, instead of each layer carrying its own private list of “valid” strings.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/schema.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; enum &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Enum&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pydantic &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; BaseModel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Metric&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, Enum):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;The only metrics a briefing is allowed to report.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    RECOVERY &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;recovery&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    SLEEP &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;sleep&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    TRAINING_LOAD &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;training_load&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Tone&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, Enum):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;The only voices a briefing is allowed to adopt.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    ENCOURAGING &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;encouraging&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    NEUTRAL &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;neutral&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    DIRECT &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;direct&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Briefing&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(BaseModel):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Agent output is parsed into this; an unknown metric or tone raises ValidationError.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    metrics: list[Metric]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    tone: Tone&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    body: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; allowed_metrics&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; set[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Every metric string the rest of the app will accept, from one place.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {m.value &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; m &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Metric}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; allowed_tones&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; set[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Every tone string the rest of the app will accept, from one place.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {t.value &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Tone}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;allowed_metrics()&lt;/code&gt; and &lt;code&gt;allowed_tones()&lt;/code&gt; are the load-bearing exports. Anything downstream that wants to know “is this a real metric” or “is this a real tone” asks the schema, not a hand-maintained constant. Keeping the two categories separate matters: a tone that wandered into the metrics list is still drift, and a union would let that cross-category mistake slip through.&lt;/p&gt;
&lt;h2&gt;Build: a scorer that parses the prompt and asserts a subset&lt;/h2&gt;
&lt;p&gt;The prompt advertises metrics and tones in prose, something like “report recovery, sleep, and training load in an encouraging voice.” That sentence and the enum above are two descriptions of the same contract, and the moment they disagree, briefings break. Because the agent’s output is parsed into the Pydantic model, an off-schema value doesn’t slip through unnoticed; it raises.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.schema &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Briefing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# agent_output is the JSON the agent produced for one briefing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;briefing &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Briefing.model_validate_json(agent_output)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ValidationError if any&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;                                                       # metric or tone is off-schema&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So drift fails loud, which sounds safe until you notice where it fails: at parse time, in production, the moment a user asks for today’s briefing. The model emits the metric the prompt promised, the schema rejects it, and the request dies with a ValidationError instead of returning anything. That’s a real failure on the user’s side, triggered by an edit to a text file no test was watching.&lt;/p&gt;
&lt;p&gt;The fix is to give the contract one source of truth and move that failure earlier. So I keep the prompt’s promises in a delimited block I can parse, pull the tokens out, and assert they’re a subset of the enum members. Drift exits non-zero in CI, so the mismatch is caught on push instead of surfacing as a failed briefing request the next morning (&lt;a href=&quot;https://en.wikipedia.org/wiki/Fail-fast_system&quot;&gt;Fail-fast system&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Here is the artifact the scorer reads. The prose rules sit next to machine-readable &lt;code&gt;METRICS:&lt;/code&gt;/&lt;code&gt;TONES:&lt;/code&gt; blocks, and the &lt;code&gt;{user_notes}&lt;/code&gt; placeholder is where saved notes get interpolated at runtime.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# fitness/briefing_prompt.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;You are the athlete&apos;s training coach. Write today&apos;s briefing from the data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;provided, and follow these rules.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Never fabricate a number; if a metric is missing, say so plainly.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Translate training load into plain coaching language instead of raw jargon.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Keep an encouraging coaching tone throughout.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;METRICS:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- recovery&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- sleep&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- training_load&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;TONES:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- encouraging&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- neutral&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- direct&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Saved athlete notes to respect:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{user_notes}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/score_prompt.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sys&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness.schema &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; allowed_metrics, allowed_tones&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; advertised_tokens&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(prompt: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; dict[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, set[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Pull the tokens the prompt promises from delimited METRICS:/TONES: blocks.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    The prompt keeps machine-readable lists alongside its prose, e.g.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        METRICS:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        - recovery&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        - sleep&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        TONES:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        - encouraging&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    Each heading sits on its own line, followed by one `- token` per line.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    re.MULTILINE lets the blocks appear anywhere in the prompt and tolerates&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    leading indentation, so the scorer isn&apos;t welded to one exact placement.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    found: dict[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, set[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;METRICS&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;TONES&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;()}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; heading &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; found:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        block &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.search(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            rf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;^&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;heading&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;:\s*$\n((?:^\s*-\s+.+$\n?)+)&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            prompt,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            re.MULTILINE,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; block:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            found[heading] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                line.split(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;-&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;].strip()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; line &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; block.group(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;).splitlines()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; line.strip().startswith(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;-&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; found&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; score&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(prompt: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Return a list of failures. Empty list means the prompt is in sync.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    failures: list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    advertised &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; advertised_tokens(prompt)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # The key assertion: everything the prompt promises must exist in the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # schema, and each category is checked against its own enum so a tone&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # listed as a metric (or vice versa) still counts as drift. Drift here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # is what breaks briefings silently downstream.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; heading, allowed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        (&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;METRICS&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, allowed_metrics()),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        (&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;TONES&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, allowed_tones()),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    ):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        drift &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; advertised[heading] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; allowed&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; drift:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            failures.append(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;prompt advertises non-schema &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;heading.lower()&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{sorted&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(drift)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Grounded prose checks, so editing the prompt can&apos;t quietly drop the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # rules the briefing depends on.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;never fabricate&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; prompt.lower():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        failures.append(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;prompt dropped the never-fabricate rule&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;training load&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; prompt.lower():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        failures.append(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;prompt dropped the training-load jargon translation&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;coaching tone&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; prompt.lower():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        failures.append(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;prompt dropped the coaching-tone instruction&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{user_notes}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; prompt:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        failures.append(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;prompt no longer wires in saved user notes&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; failures&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    with&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; open&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;fitness/briefing_prompt.txt&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; f:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        failures &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; score(f.read())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; f &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; failures:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;        print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;FAIL: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, file&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;sys.stderr)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; failures &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __name__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    sys.exit(main())  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# non-zero exit -&gt; CI fails loud&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the direction of the check. I assert the advertised tokens are a subset of the schema, not the reverse. The schema is allowed to know about a metric the prompt hasn’t started using yet; the prompt is never allowed to promise something the schema can’t validate. That asymmetry is the whole point: the machine-readable contract is the ceiling.&lt;/p&gt;
&lt;h2&gt;Build: a hermetic test fixture for the deterministic core&lt;/h2&gt;
&lt;p&gt;The deterministic core got real tests, but the detail that taught me something was how the first CI run failed. My security tests passed on my machine and blew up on a clean checkout, because they were silently reading my actual local Garmin database. On my laptop that file exists, so the tests looked green; on a fresh runner it doesn’t, so they fell over. Green tests that aren’t actually exercising the code are their own kind of silent failure, a plausible signal hiding a broken state (&lt;a href=&quot;https://aipatternbook.com/silent-failure&quot;&gt;aipatternbook: Silent Failure&lt;/a&gt;). That’s the exact failure hermetic testing is designed to prevent: a test’s result shouldn’t depend on the machine or the person running it, so it brings its own throw-away dependencies and touches no network (&lt;a href=&quot;https://testing.googleblog.com/2012/10/hermetic-servers.html&quot;&gt;Google Testing Blog: Hermetic Servers&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The fixture below creates a fresh SQLite file in a temp directory, seeds it with known rows, and points the app’s database path at it for the duration of the test. Nothing reads my real data, and nothing reaches the network.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/conftest.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sqlite3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pytest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;@pytest.fixture&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; fitness_db&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(tmp_path, monkeypatch):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;A seeded, throw-away database. Created fresh, discarded after, no network.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    db_path &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tmp_path &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;fitness.db&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    conn &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sqlite3.connect(db_path)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    conn.executescript(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        CREATE TABLE daily (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            day TEXT PRIMARY KEY,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            recovery INTEGER,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            sleep_minutes INTEGER,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            training_load TEXT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    conn.execute(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;INSERT INTO daily VALUES (&apos;2026-06-06&apos;, 72, 444, &apos;moderate&apos;)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    conn.commit()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    conn.close()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Point the app at the temp DB so no test can touch the real one.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setenv(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;FITNESS_DB&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(db_path))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db_path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fixture lives in &lt;code&gt;conftest.py&lt;/code&gt; so pytest shares it across files, but the test itself belongs in a normal test module; pytest doesn’t collect tests out of &lt;code&gt;conftest.py&lt;/code&gt;. The test asks for the &lt;code&gt;fitness_db&lt;/code&gt; fixture by name and reads the seeded row through the app’s own data layer.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_snapshot.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; fitness &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_snapshot_reads_seeded_row&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(fitness_db):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    row &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; db.snapshot_for(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;2026-06-06&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; row.recovery &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 72&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; round&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(row.sleep_minutes &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 60&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 7.4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That data layer is the small module under test. The detail that makes the fixture work is that &lt;code&gt;snapshot_for&lt;/code&gt; reads &lt;code&gt;FITNESS_DB&lt;/code&gt; inside the call, not at import time, so the fixture’s &lt;code&gt;monkeypatch.setenv&lt;/code&gt; is already in effect when the path is resolved.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# fitness/db.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sqlite3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; typing &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; NamedTuple&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Snapshot&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(NamedTuple):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    day: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    recovery: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    sleep_minutes: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    training_load: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; snapshot_for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(day: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; Snapshot:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Read one day&apos;s row from whatever database FITNESS_DB points at.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    Resolving the env var here (not at import) means tests can repoint it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    before the call without reloading the module.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    db_path &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; os.environ[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;FITNESS_DB&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    conn &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sqlite3.connect(db_path)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    try&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        cur &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; conn.execute(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;SELECT day, recovery, sleep_minutes, training_load &quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;FROM daily WHERE day = ?&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            (day,),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        row &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; cur.fetchone()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    finally&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        conn.close()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; row &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        raise&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; KeyError&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(day)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Snapshot(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;row)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What actually surfaced the bug was the clean runner with no copy of my local database, not the coverage gate; coverage measures which lines executed, it can’t tell that a test read the wrong file. What the coverage gate does is keep untested paths from hiding, so the code that breaks on a fresh checkout is forced to run somewhere I’ll see it.&lt;/p&gt;
&lt;h2&gt;Wire it: lint, the coverage gate, and the scorer on every push&lt;/h2&gt;
&lt;p&gt;The three checks run together so nothing ships without passing all of them. Linting and the hermetic test suite cover the deterministic code, the coverage gate forces the tested paths to actually run, and the prompt scorer guards the one surface that doesn’t compile. Putting the scorer in the same pipeline as the tests is deliberate: the prompt is a build artifact, so it gets gated like one.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# .github/workflows/ci.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name: ci&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;on: [push, pull_request]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;jobs:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  check:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    runs-on: ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    steps:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - uses: actions/checkout@v4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - uses: actions/setup-python@v5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        with:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          python-version: &quot;3.12&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - run: pip install -e &quot;.[dev]&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - name: Lint&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        run: ruff check .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - name: Tests with coverage gate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        run: pytest --cov=fitness --cov-fail-under=85&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        # The runner has internet by default, but the hermetic fixtures bring&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        # their own seeded DBs and never make network calls, so the result&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        # doesn&apos;t depend on the environment.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - name: Score the prompt against the schema&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        run: python -m fitness.score_prompt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        # Exits non-zero on drift, failing the job loud.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each step fails the job on its own, so a drifted prompt and an under-covered module are both blocking, not advisory. The &lt;code&gt;ruff&lt;/code&gt; and &lt;code&gt;pytest-cov&lt;/code&gt; those steps depend on aren’t standard library, so they live in a &lt;code&gt;dev&lt;/code&gt; extra that the &lt;code&gt;pip install -e &amp;quot;.[dev]&amp;quot;&lt;/code&gt; step pulls in.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# pyproject.toml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[project.optional-dependencies]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dev = [&quot;ruff&quot;, &quot;pytest&quot;, &quot;pytest-cov&quot;, &quot;pydantic&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Verify it: catch the drift and watch the test fail&lt;/h2&gt;
&lt;p&gt;Two failures prove the wiring. First, introduce real drift by removing a member from the enum while the prompt still advertises it, and run the scorer.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; python&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; fitness.score_prompt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;FAIL:&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; prompt&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; advertises&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; non-schema&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; metrics:&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&apos;training_load&apos;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; $?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The scorer found that the prompt still promises &lt;code&gt;training_load&lt;/code&gt; after it was dropped from &lt;code&gt;Metric&lt;/code&gt;, and exited non-zero, so CI stops here instead of letting the next briefing request crash on a ValidationError in production. Second, make the seeded value and the assertion disagree, by changing one of them, and confirm the fixture isolates the test so the failure is identical everywhere.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; pytest&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; tests/test_snapshot.py&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -q&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;F&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;E&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;   assert&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 70&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 72&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;E&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    +&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;  where&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 70&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; Snapshot&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(day&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&apos;2026-06-06&apos;,&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; recovery&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;70,&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;.recovery&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; failed&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; 0.04s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The test reads only the seeded row, so a wrong assertion fails the same way on my laptop and on a clean runner. That equivalence is the whole point of making it hermetic.&lt;/p&gt;
&lt;h2&gt;Version-driven, idempotent releases&lt;/h2&gt;
&lt;p&gt;The release workflow is built so that merging is not the same thing as shipping. Once CI is green on the default branch, it checks the version in the project file and cuts a GitHub Release only if that version is new. A normal merge is a no-op; bumping the version is the act that ships. This makes releasing idempotent, in that running the workflow again on the same version does nothing, so there’s no risk of duplicate or accidental releases from an ordinary merge. The version number is the release key, which keeps the rule for changing it honest: I follow semantic versioning, where the number communicates the nature of the change, and &lt;code&gt;0.y.z&lt;/code&gt; explicitly signals early development where the API isn’t yet stable (&lt;a href=&quot;https://semver.org/&quot;&gt;Semantic Versioning 2.0.0&lt;/a&gt;). v0.1.0 is exactly that signal, a first public cut that says “real software now, but still moving.”&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;I want to widen coverage into the briefing-generation and chat paths, which are the parts still leaning mostly on the prompt scorer rather than executed tests. And I want to treat prompt changes as releases in their own right, since a change to the briefing prompt changes the product as surely as a code change does, and it should go through the same version-and-ship gate.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents&quot;&gt;Anthropic: Demystifying evals for AI agents&lt;/a&gt; — groundedness, coverage, and graders for turning “is this good” into concrete outcomes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.pydantic.dev/latest/api/standard_library_types/&quot;&gt;Pydantic: Standard Library Types&lt;/a&gt; — enum validation accepts only real members of the enum.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aipatternbook.com/silent-failure&quot;&gt;aipatternbook: Silent Failure&lt;/a&gt; — systems that keep running while returning plausible-but-wrong output.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Fail-fast_system&quot;&gt;Fail-fast system&lt;/a&gt; — detect violations early and stop, rather than continuing in a broken state.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://testing.googleblog.com/2012/10/hermetic-servers.html&quot;&gt;Google Testing Blog: Hermetic Servers&lt;/a&gt; — tests that bring their own dependencies and don’t depend on the environment.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://semver.org/&quot;&gt;Semantic Versioning 2.0.0&lt;/a&gt; — version numbers that communicate the nature of a change; &lt;code&gt;0.y.z&lt;/code&gt; for initial development.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[local-fitness] Treat the agent as code: score the prompt, test the core, ship CI (#16) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/6279ddf667329c9e405b512427360d3e66c7dec4&quot;&gt;6279ddf&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[local-fitness] docs: rewrite README as a shareable OSS project; add MIT LICENSE (#17) (&lt;a href=&quot;https://github.com/natejswenson/local-fitness-dude/commit/8b35e2c30d852b1d426f6535e75e5d7406c3bac2&quot;&gt;8b35e2c&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>A skill is code: scoring the SKILL.md and testing the glue around it</title><link>https://natejswenson.com/devlog/ghostwriter/v0.0.1/</link><guid isPermaLink="true">https://natejswenson.com/devlog/ghostwriter/v0.0.1/</guid><description>A Claude skill is software, so I held the ghostwriter skill to software&apos;s bar: a scorer that structurally grades the SKILL.md as a CI gate, 100% line coverage on its glue scripts by mocking the network and browser boundaries, and an honest line about what coverage doesn&apos;t prove.</description><pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;v0.0.1 is the release where the ghostwriter skill started being treated like code instead of a clever text file. A Claude skill is a folder with a &lt;code&gt;SKILL.md&lt;/code&gt; plus the scripts it drives, and that is software whether or not it compiles (&lt;a href=&quot;https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills&quot;&gt;Anthropic: Equipping agents with Agent Skills&lt;/a&gt;). So it got the things software gets: a scorer that grades the &lt;code&gt;SKILL.md&lt;/code&gt; against a checklist and passes 8/8, a pytest suite covering all five Python scripts at 100% line coverage across 94 tests, a shellcheck lint on the one bash script, all three wired into CI on every push and PR to main, and a real release cut with a version field, a changelog entry, an annotated tag, and a GitHub release.&lt;/p&gt;
&lt;p&gt;The interesting part isn’t the green checkmarks. A prompt and a pile of integration glue are exactly the two kinds of code people skip testing, and each one needs a different tool to get honest coverage. Here is the end-to-end build: what to check, how to score it, how to test the glue without real I/O, and how to gate all of it in CI.&lt;/p&gt;
&lt;h2&gt;Setup: what a &lt;a href=&quot;http://SKILL.md&quot;&gt;SKILL.md&lt;/a&gt; must contain&lt;/h2&gt;
&lt;p&gt;The body of a &lt;code&gt;SKILL.md&lt;/code&gt; is a prompt. There’s no compiler to tell you it’s missing the guardrail that keeps it from publishing without approval, and no type checker to notice you dropped the line declaring its operating modes. The failure mode is silent: the skill still runs, it just runs without the rule you thought you’d written. Anthropic’s own guidance treats the &lt;code&gt;SKILL.md&lt;/code&gt; as a structured artifact with required frontmatter and a defined shape (&lt;a href=&quot;https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills&quot;&gt;Anthropic: Equipping agents with Agent Skills&lt;/a&gt;), and anything with a required shape can be checked for that shape.&lt;/p&gt;
&lt;p&gt;So the requirements are concrete things the file must literally contain. Anthropic requires &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; in the frontmatter; I also require &lt;code&gt;version&lt;/code&gt;, since a skill that ships releases needs one. The body has to declare the skill’s operating modes, state the never-publish-without-approval guardrail, reference the no-automated-posting compliance rule, and name the voice inputs it learns the user’s style from. Each one is a string or a key you can search for, which means each one is a test.&lt;/p&gt;
&lt;h2&gt;Build the scorer&lt;/h2&gt;
&lt;p&gt;The scorer turns each requirement into a predicate over the file’s text and exits non-zero the moment one fails, which is what makes it a CI gate rather than a report. The real skill runs eight such checks; below are the load-bearing ones. The harness drops into any skill repo cleanly; swap the body predicates for your skill’s own required phrases, since the four body checks below hardcode ghostwriter’s exact policy strings.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&quot;&quot;Score a SKILL.md against structural requirements. Exit non-zero so CI can gate.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sys&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;REQUIRED_FRONTMATTER &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Each check pairs a human-readable label with a predicate over the file text.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; _frontmatter&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(text: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Grab the leading ---...--- block so a key in the body can&apos;t satisfy a check.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    m &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; re.match(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;r&quot;^---\n(.*?)\n---&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, text, re.S)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; m.group(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; m &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;CHECKS &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;frontmatter declares name, description, version&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # [^\S\n]* matches spaces/tabs but not newlines, so an empty value fails.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;all&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(re.search(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;rf&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;^&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;k&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;:[^\S\n]*\S&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, _frontmatter(t), re.M)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;                      for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; k &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; REQUIRED_FRONTMATTER),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;declares its operating modes&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;operating modes&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t.lower(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;states the never-publish-without-approval guardrail&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;never publish without approval&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t.lower(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;references the no-automated-posting compliance rule&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;automated posting&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t.lower(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;names the voice inputs it learns from&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        lambda&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;voice&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t.lower() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;past posts&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; t.lower(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; score&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(text: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; tuple[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, list[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    failed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [label &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; label, ok &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; CHECKS.items() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ok(text)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(CHECKS) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(failed), failed&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(path: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    passed, failed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; score(Path(path).read_text(encoding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;SKILL.md score: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;passed&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(CHECKS)&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; label &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; failed:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;        print&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;  FAIL: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;label&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Non-zero exit on any failure is what turns this into a CI gate, not a report.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; failed &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __name__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;__main__&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    raise&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; SystemExit&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(main(sys.argv[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; len&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(sys.argv) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; else&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;SKILL.md&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are structural checks, not behavioral ones. The scorer proves the rule is written down, not that the skill obeys it at runtime. That’s a real limit, and naming it is the point of the next-steps section. But “the guardrail is present and CI will fail if someone deletes it” is a meaningfully stronger guarantee than a prompt nobody verifies.&lt;/p&gt;
&lt;h2&gt;Mock the boundary to cover the glue&lt;/h2&gt;
&lt;p&gt;The five scripts the skill drives are almost pure glue: a LinkedIn client that makes network calls, a Playwright-based image renderer that drives a headless browser, an OAuth callback server that wants to bind a socket and open a browser tab. Glue like this is what teams point at when they say “you can’t really unit-test this,” and they’re half right. You can’t test it by letting it touch the real network, the real browser, or a real socket on every CI run.&lt;/p&gt;
&lt;p&gt;The way through is test doubles at the architectural boundary. Martin Fowler’s taxonomy is the useful vocabulary: a stub answers queries with canned responses, a mock also verifies the calls it expected, and a fake is a working stand-in like an in-memory implementation (&lt;a href=&quot;https://martinfowler.com/bliki/TestDouble.html&quot;&gt;Martin Fowler: Test Double&lt;/a&gt;; &lt;a href=&quot;https://martinfowler.com/articles/mocksArentStubs.html&quot;&gt;Martin Fowler: Mocks Aren’t Stubs&lt;/a&gt;). The discipline is to put those doubles exactly at the edge where your code meets the outside world. Everything inside the boundary, which is the logic you actually wrote, runs for real. Take the LinkedIn client as the representative network case; the browser and socket doubles follow the same pattern. The five scripts live in a &lt;code&gt;ghostwriter/&lt;/code&gt; package, so this file is &lt;code&gt;ghostwriter/linkedin.py&lt;/code&gt;; tests import it as &lt;code&gt;from ghostwriter import linkedin&lt;/code&gt;, which is exactly what the &lt;code&gt;--cov=ghostwriter&lt;/code&gt; flag in CI measures.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ghostwriter/linkedin.py: the network glue under test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; httpx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# /v2/ugcPosts is LinkedIn&apos;s legacy share endpoint (the current one is /rest/posts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# with a LinkedIn-Version header); ghostwriter still targets the legacy API here.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;API &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;https://api.linkedin.com/v2/ugcPosts&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; publish_post&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(token: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, author_urn: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, text: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;POST a share to LinkedIn and return the created post id.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    resp &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; httpx.post(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        API,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        headers&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;Authorization&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Bearer &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;token&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;            # ugcPosts requires protocol 2.0.0 on every request.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;X-Restli-Protocol-Version&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;2.0.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        json&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;author&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: author_urn,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;lifecycleState&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;PUBLISHED&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;specificContent&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;                &quot;com.linkedin.ugc.ShareContent&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;                    &quot;shareCommentary&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: text},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;                    &quot;shareMediaCategory&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;NONE&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;            },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;            &quot;visibility&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;com.linkedin.ugc.MemberNetworkVisibility&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;PUBLIC&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    resp.raise_for_status()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # The canonical place to read the new id is this response header.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; resp.headers[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;x-restli-id&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The test replaces &lt;code&gt;httpx.post&lt;/code&gt; with a double that records the request and hands back a canned response, so the assertions verify both that the glue built the right call and that it read the new id from the reply the way callers depend on. A second test drives the error branch, behavior the success test never asserts.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# test_linkedin.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; httpx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pytest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ghostwriter &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; linkedin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_publish_post_sends_expected_payload&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(monkeypatch):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    captured &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # A stub+spy at the network boundary: return a canned response AND&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # record the call so the test can assert on it afterward.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; fake_post&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(url, headers, json):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        share &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; json[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;specificContent&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;com.linkedin.ugc.ShareContent&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        captured.update(url&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;url, auth&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;headers[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                        text&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;share[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;shareCommentary&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        request &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; httpx.Request(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, url)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # The new id comes back in this response header.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; httpx.Response(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;201&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, headers&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;x-restli-id&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;urn:li:share:123&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                              request&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;request)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setattr(linkedin.httpx, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, fake_post)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    post_id &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; linkedin.publish_post(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;tok&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;urn:li:person:me&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;hello world&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Behavior verification: assert on the call the glue made to the collaborator.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; captured[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; linkedin.API&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; captured[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;auth&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;Bearer tok&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; captured[&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;hello world&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # State verification: it read the new id from the header the way callers rely on.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; post_id &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;urn:li:share:123&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_publish_post_raises_on_api_error&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(monkeypatch):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; fake_post&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(url, headers, json):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; httpx.Response(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;401&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, request&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;httpx.Request(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, url))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setattr(linkedin.httpx, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, fake_post)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pytest.raises(httpx.HTTPStatusError):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        linkedin.publish_post(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;bad&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;urn:li:person:me&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;nope&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is two tests covering both branches without touching the real network. The same pattern faked the Playwright page so the renderer thought it had a browser, and stood the OAuth callback server up against a loopback double so it never opened a tab or bound a real port. That is how all five scripts reached 100% line coverage across 94 tests.&lt;/p&gt;
&lt;h2&gt;Wire it into CI&lt;/h2&gt;
&lt;p&gt;The scorer, the coverage gate, and the bash lint all run on every push and pull request to main. The coverage gate is a flag, not a habit: &lt;code&gt;--cov-fail-under=100&lt;/code&gt; fails the job if a single line goes unexercised, so the number can’t quietly erode. The bash script gets shellcheck instead of a coverage number, because Python’s coverage tool can’t measure it and pretending otherwise would be dishonest.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# .github/workflows/ci.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name: ci&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;on:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  push:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    branches: [main]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  pull_request:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    branches: [main]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;jobs:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  quality:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    runs-on: ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    steps:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - uses: actions/checkout@v4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - uses: actions/setup-python@v5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        with:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          python-version: &quot;3.12&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - name: Install deps&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        run: pip install -r requirements.txt pytest pytest-cov&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      # Bash gets a static analyzer, not a fake coverage number. Preinstalled on the runner.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - name: Shellcheck the release script&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        run: shellcheck scripts/release-radar.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      # 100% line coverage or the job fails. The gate is the flag, not discipline.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - name: Tests with coverage gate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        run: pytest --cov=ghostwriter --cov-report=term-missing --cov-fail-under=100&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      # Score the prompt itself; the scorer&apos;s non-zero exit blocks the merge.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - name: Score SKILL.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        run: python scripts/score_skill.py SKILL.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ShellCheck is a static analyzer that catches the quoting bugs, unset variables, and pitfalls that make shell scripts fail in ways the shell reports cryptically (&lt;a href=&quot;https://www.shellcheck.net/&quot;&gt;ShellCheck&lt;/a&gt;), and it ships on GitHub’s Ubuntu runners, so the lint step needs no install. Each step exits non-zero on failure, so any one of the three can stop a merge on its own.&lt;/p&gt;
&lt;h2&gt;Verify: a missing guardrail fails the gate&lt;/h2&gt;
&lt;p&gt;The proof that the scorer is a gate and not decoration is watching it go red. Delete the guardrail line and the exit code flips to 1, which is exactly what the CI step keys on. The run below uses the trimmed 5-check version shown above; the real skill scores 8/8.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; python&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; scripts/score_skill.py&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; SKILL.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;SKILL.md&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; score:&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; 5/5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Drop the approval guardrail, and the gate turns red. The I flag makes the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# match case-insensitive, mirroring the scorer&apos;s own t.lower() comparison, so a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# guardrail written in sentence case is still deleted rather than silently kept:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; sed&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &apos;/never publish without approval/Id&apos;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; SKILL.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; python&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; scripts/score_skill.py&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; SKILL.md&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;SKILL.md&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; score:&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; 4/5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  FAIL:&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; states&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; never-publish-without-approval&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; guardrail&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; $?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That exit code is the whole mechanism. If a future edit drops the rule that keeps the skill from posting without a human saying yes, the merge can’t land.&lt;/p&gt;
&lt;p&gt;One honest caveat sits underneath all of this. Coverage is a negative metric, not a positive one. Fowler’s point is that coverage is good at finding code with no tests, but a high number says little about whether the tests assert anything useful, since 100% is reachable with assertion-free tests that exercise lines without checking results (&lt;a href=&quot;https://martinfowler.com/bliki/TestCoverage.html&quot;&gt;Martin Fowler: Test Coverage&lt;/a&gt;). So 100% here means every Python line ran under test with real assertions around the logic. It does not mean the scripts are bug-free, and it says nothing at all about the bash script, which is why that one carries shellcheck’s name instead of a borrowed number.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The scorer’s honest limitation is that it’s structural. The follow-up is to extend it toward behavioral checks: small fixtures that feed the skill a voice profile and assert the generated drafts honor the voice rules, so CI verifies the skill does the right thing and not only that the rule is written down. The other thread is keeping the version-and-changelog discipline going, treating each future change as a real release under semantic versioning, where 0.x signals the surface is still allowed to move while the project finds its shape (&lt;a href=&quot;https://semver.org/&quot;&gt;Semantic Versioning 2.0.0&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills&quot;&gt;Anthropic: Equipping agents for the real world with Agent Skills&lt;/a&gt; — a skill is a folder of a &lt;a href=&quot;http://SKILL.md&quot;&gt;SKILL.md&lt;/a&gt; plus scripts, a structured artifact with required frontmatter and shape.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://martinfowler.com/bliki/TestDouble.html&quot;&gt;Martin Fowler: Test Double&lt;/a&gt; — the taxonomy of stubs, mocks, fakes, and spies for standing in for real collaborators.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://martinfowler.com/articles/mocksArentStubs.html&quot;&gt;Martin Fowler: Mocks Aren’t Stubs&lt;/a&gt; — the distinction between state verification and behavior verification at test boundaries.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://martinfowler.com/bliki/TestCoverage.html&quot;&gt;Martin Fowler: Test Coverage&lt;/a&gt; — coverage finds untested code but is a poor measure of test quality; 100% is reachable with assertion-free tests.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.shellcheck.net/&quot;&gt;ShellCheck&lt;/a&gt; — static analysis for shell scripts that catches quoting bugs, unset variables, and pitfalls before runtime.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://semver.org/&quot;&gt;Semantic Versioning 2.0.0&lt;/a&gt; — major version zero is for initial development where the public surface may still change.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[linkedin-ghostwriter] Treat the skill as code: score &lt;a href=&quot;http://SKILL.md&quot;&gt;SKILL.md&lt;/a&gt;, 100% script coverage, v0.0.1 (#3) (&lt;a href=&quot;https://github.com/natejswenson/linkedin-ghostwriter/commit/ebe0b09e2bec0394dd8523d976a1f4fb14ed8cfb&quot;&gt;ebe0b09&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>You already have an image renderer: HTML and Mermaid into PNGs on your own machine</title><link>https://natejswenson.com/devlog/ghostwriter/2026-06-05/</link><guid isPermaLink="true">https://natejswenson.com/devlog/ghostwriter/2026-06-05/</guid><description>Ghostwriter can now attach a rendered diagram or card to a post, and it renders entirely through a local headless browser instead of a third-party service. The two lessons worth keeping are that a browser you already run is a capable image renderer, and that personal styling belongs in config that never touches the shared repo.</description><pubDate>Fri, 05 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;Ghostwriter writes LinkedIn posts in my voice. This change lets a post optionally carry a visual: a technical diagram written in Mermaid, or a designed card built from plain HTML and CSS for a single punchy idea. Both render locally through a headless Chromium driven by Playwright, into a high-DPI PNG, so the content never leaves my machine for a third-party renderer. It stays opt-in. Text-only posts are still the default, and an image only attaches after I approve the rendered PNG. On the publish side, the poster runs the LinkedIn image-upload flow only when an image is actually provided, so the image flow never runs on the text-only path. A same-day follow-up made rendered files auto-open in the image viewer instead of being written and forgotten, added a reusable date and deadline card type that makes the date the hero, and moved the repo onto a branch-and-PR workflow.&lt;/p&gt;
&lt;p&gt;The feature is small. The parts worth writing about are how the rendering works without a hosted service, and how I kept my personal look out of a repo other people can clone. Here is the full build, from install to a test that protects the text-only path.&lt;/p&gt;
&lt;h2&gt;Setup: install a browser you already trust&lt;/h2&gt;
&lt;p&gt;The whole approach rests on one dependency, Playwright, which ships and manages its own browser binaries so you do not have to find or pin a system Chrome. Install the library, then ask it to fetch Chromium along with the OS-level libraries the browser needs to run headless (&lt;a href=&quot;https://playwright.dev/python/docs/browsers&quot;&gt;Playwright: Installing browsers&lt;/a&gt;). The &lt;code&gt;--with-deps&lt;/code&gt; flag is what makes this reproducible on a fresh machine or in CI, where the bare browser would otherwise fail to start.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;python&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; venv&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; .venv&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;source&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; .venv/bin/activate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;pip&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; playwright&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;              # core dependency&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;pip&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; pytest&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; pillow&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;          # for the tests below (pillow reads PNG dimensions)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;python&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; playwright&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --with-deps&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; chromium&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   # the browser binary + its OS libs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# vendor Mermaid locally so the render needs no CDN (pin the major version)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; vendor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -L&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; vendor/mermaid.min.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the personal brand file is generated locally and never committed (see below)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;diagram.css&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; .gitignore&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is the entire infrastructure. No renderer service, no API key, no account. The cost is carrying a browser binary, which Playwright installs and updates for you.&lt;/p&gt;
&lt;h2&gt;A headless browser is a capable image renderer&lt;/h2&gt;
&lt;p&gt;The instinct when you need “HTML turned into an image” is to reach for a hosted screenshot or diagram API. You don’t have to. A headless browser already lays out HTML and CSS exactly the way a real browser would, and Playwright can screenshot the result. That covers both cases here: a styled card is just an HTML document, and a Mermaid diagram is HTML and CSS once Mermaid’s client library has rendered it in the page. The browser does the hard part, which is layout and font rendering, and you take a picture of the output.&lt;/p&gt;
&lt;p&gt;The one detail that matters for quality is pixel density. By default a screenshot uses one image pixel per CSS pixel, which looks soft on retina displays and worse when someone zooms a LinkedIn image. Playwright lets you set &lt;code&gt;device_scale_factor&lt;/code&gt; on the browser context to emulate a high-DPI display, so a value of 2 renders two device pixels for every CSS pixel and you get a crisp, retina-grade PNG (&lt;a href=&quot;https://playwright.dev/python/docs/emulation&quot;&gt;Playwright: Emulation&lt;/a&gt;). Screenshotting a specific element instead of the whole page crops the output to just the card, with no surrounding whitespace to trim (&lt;a href=&quot;https://playwright.dev/python/docs/api/class-locator&quot;&gt;Playwright: Locator API&lt;/a&gt;).&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ghostwriter/render.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __future__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; annotations  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# so `str | None` works on Python 3.7+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; playwright.sync_api &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; render_to_png&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(html: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, selector: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, out_png: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, scale: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;                  wait_for: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None) -&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Render an HTML string to a cropped, high-DPI PNG with local headless Chromium.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    with&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; sync_playwright() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        browser &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; p.chromium.launch()  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# headless by default; nothing leaves the machine&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # device_scale_factor=2 =&gt; 2 device pixels per CSS pixel, i.e. retina-grade output&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        context &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; browser.new_context(device_scale_factor&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;scale)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; context.new_page()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page.set_content(html)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;        # Wait on a concrete render signal: the node we are about to shoot must exist.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page.wait_for_selector(wait_for &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;or&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; selector)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        page.locator(selector).screenshot(path&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;out_png)   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# crop to just that element&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        browser.close()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The wait is what guarantees a finished diagram, and it has to key off the rendered output, not the network. The tempting choice, &lt;code&gt;wait_until=&amp;quot;networkidle&amp;quot;&lt;/code&gt;, is the wrong tool here: it tracks network traffic, not the JavaScript that draws the diagram, and Playwright explicitly discourages it (&lt;a href=&quot;https://playwright.dev/python/docs/api/class-page&quot;&gt;Playwright: Page API&lt;/a&gt;). Mermaid renders client-side after the page loads, and if its library is vendored locally there is barely any network traffic, so networkidle resolves before the SVG exists and you screenshot an empty container. Waiting for the actual node, the &lt;code&gt;.card&lt;/code&gt; for a static card or the injected &lt;code&gt;.mermaid svg&lt;/code&gt; once Mermaid finishes, is what holds the screenshot until the picture is really there.&lt;/p&gt;
&lt;h2&gt;Personal styling is config, and config stays out of the shared repo&lt;/h2&gt;
&lt;p&gt;The card and diagram styling, including the byline that signs each image, is the part that makes the output look like mine. It would be tempting to bake that into the rendering code, and that’s exactly the wrong place for it. Styling and byline live in a gitignored &lt;code&gt;diagram.css&lt;/code&gt;, so a fresh clone of the tool starts from a neutral template with no personal branding committed. The byline itself is a single CSS variable that auto-applies to the bottom of every card and diagram, set once rather than repeated per template.&lt;/p&gt;
&lt;p&gt;This is the twelve-factor config principle applied to a personal tool: config is everything that varies between users or deploys, and it should be stored outside the code, not embedded in it (&lt;a href=&quot;https://12factor.net/config&quot;&gt;12factor: Config&lt;/a&gt;). The methodology even gives a litmus test, which is whether you could open-source the codebase at any moment without exposing anything that should stay private. A gitignored brand file passes that test cleanly, and keeping a checked-in &lt;code&gt;.example&lt;/code&gt; neutral template is the standard way to ship the shape of the config without the contents (&lt;a href=&quot;https://blog.gitguardian.com/secrets-api-management/&quot;&gt;GitGuardian: Secrets management best practices&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The neutral template is what ships in the repo. It defines the variables every template reads, with empty or generic values.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* diagram.css.example  (checked in, neutral)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   Copy to diagram.css (gitignored) and personalize. */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:root&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --byline&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;                         &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* e.g. &quot;— Jane Doe · jane.dev&quot; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --brand-fg&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #111&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --brand-bg&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #fff&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --brand-accent&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #2563eb&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  --brand-font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; system-ui&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;sans-serif&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--brand-fg&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; background&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--brand-bg&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; font-family&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--brand-font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* One rule signs every image; set --byline once instead of editing each template.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   With the neutral default (--byline: &quot;&quot;), this renders an empty signature line:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   the box and its margin still reserve space at the bottom of every card. That is&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   intentional for the template; if you want zero footprint until you brand it,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;   delete this rule here and move it into your personal diagram.css. */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.card&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;::after&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  content&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--byline&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  display&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; block&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  margin-top&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1.5rem&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  color&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--brand-accent&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 600&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0.85rem&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;--brand-font&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rendering code stays generic. It loads the personal brand if it exists and falls back to the neutral template otherwise, then inlines it so the screenshot picks it up with no network fetch.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ghostwriter/brand.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;BRAND &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;diagram.css&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)            &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# gitignored, personal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;NEUTRAL &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;diagram.css.example&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# checked in, generic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; brand_css&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Personal brand if present; otherwise the neutral checked-in template.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    path &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; BRAND &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; BRAND.exists() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; NEUTRAL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; path.read_text(encoding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; wrap_card&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(body_html: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Inline the brand stylesheet so render_to_png needs no external request.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    body_html must be the `.card` element itself, e.g.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&amp;#x3C;div class=&apos;card&apos;&gt;...&amp;#x3C;/div&gt;&quot;, because the render selector and the brand&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    CSS both target `.card`; bare inner content would never match and the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    screenshot&apos;s wait_for_selector would hang.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&amp;#x3C;!doctype html&gt;&amp;#x3C;html&gt;&amp;#x3C;head&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&amp;#x3C;style&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;brand_css()&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&amp;#x3C;/style&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&amp;#x3C;/head&gt;&amp;#x3C;body&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;body_html&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&amp;#x3C;/body&gt;&amp;#x3C;/html&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rendering code is shared and generic; the look is supplied. That split is what lets the same tool belong to anyone who clones it.&lt;/p&gt;
&lt;p&gt;The Mermaid case is the same idea with one extra step: the diagram source is not HTML yet, so the page has to run Mermaid’s client library to turn it into an SVG before there is anything to screenshot. I vendor &lt;code&gt;mermaid.min.js&lt;/code&gt; locally and inline it for the same reason I inline the CSS, so the render needs no network at all. The diagram source goes into a &lt;code&gt;&amp;lt;pre class=&amp;quot;mermaid&amp;quot;&amp;gt;&lt;/code&gt;, and &lt;code&gt;mermaid.run()&lt;/code&gt; injects an &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; inside that node and marks it processed (&lt;a href=&quot;https://mermaid.js.org/config/usage.html&quot;&gt;Mermaid: Usage&lt;/a&gt;). Because &lt;code&gt;mermaid.run()&lt;/code&gt; is asynchronous, the render function’s &lt;code&gt;wait_for=&amp;quot;.mermaid svg&amp;quot;&lt;/code&gt; is exactly what holds the screenshot until that SVG has been injected.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ghostwriter/mermaid.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; pathlib &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ghostwriter.brand &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; brand_css&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;MERMAID_JS &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Path(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;vendor/mermaid.min.js&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# vendored, no CDN (see setup)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; wrap_mermaid&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(diagram_src: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Inline mermaid.js + brand CSS; mermaid.run() draws the SVG after load.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Read lazily, not at import, so importing the module never crashes when the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # vendored file is missing; the error only surfaces if you actually render.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    mermaid_js &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; MERMAID_JS.read_text(encoding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # Escape the source before it lands in innerHTML: node labels containing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # &amp;#x3C;, &gt;, or &amp;#x26; would otherwise be parsed as HTML before Mermaid reads them.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    safe_src &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; html.escape(diagram_src)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&amp;#x3C;!doctype html&gt;&amp;#x3C;html&gt;&amp;#x3C;head&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&amp;#x3C;style&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;brand_css()&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&amp;#x3C;/style&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&amp;#x3C;script&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;mermaid_js&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&amp;#x3C;/script&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&amp;#x3C;/head&gt;&amp;#x3C;body&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        f&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&amp;#x3C;pre class=&apos;mermaid&apos;&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;safe_src&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&amp;#x3C;/pre&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&amp;#x3C;script&gt;mermaid.initialize(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;{startOnLoad&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; false}&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;); mermaid.run();&amp;#x3C;/script&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;        &quot;&amp;#x3C;/body&gt;&amp;#x3C;/html&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# rendering a diagram: wait for the SVG mermaid injects, then crop to it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;html &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; wrap_mermaid(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;graph TD; A[draft] --&gt; B[render] --&gt; C[approve]&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;render_to_png(html, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.mermaid&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;out/diagram.png&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, wait_for&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.mermaid svg&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Make the optional path optional, and leave the existing path alone&lt;/h2&gt;
&lt;p&gt;There’s a quieter discipline in this change that I want to name. Adding the image feature could not be allowed to change how text-only posts behave, because text-only is the default and the path I trust. &lt;code&gt;publish_post&lt;/code&gt; itself grew a new parameter and a branch, but the text-only request is the same call it always made, and the image steps run only when an image is provided. That is the spirit of the open-closed principle, where you add new behavior without disturbing the code path that already works (&lt;a href=&quot;https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle&quot;&gt;Wikipedia: Open–closed principle&lt;/a&gt;). When a new feature is genuinely opt-in, the safest version is the one where the default invocation still issues the same request it always did, with the new work isolated to a branch the trusted path never enters.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# ghostwriter/publish.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; __future__ &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; annotations  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# so `str | None` works on Python 3.7+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ghostwriter &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; linkedin  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# thin client around the LinkedIn API&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; publish_post&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(text: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, image_png: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;    &quot;&quot;&quot;Text-only is the default and untouched. Image upload runs only when given one.&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; image_png &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; linkedin.create_text_post(text)         &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the same call as before&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;    # opt-in branch: the extra image round-trips live entirely here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    asset &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; linkedin.register_image_upload()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    linkedin.upload_binary(asset.upload_url, image_png)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; linkedin.create_image_post(text, asset.urn)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;linkedin.*&lt;/code&gt; calls here are a placeholder for LinkedIn’s actual asset flow: register an upload to get an upload URL and an asset URN, PUT the image bytes to that URL, then create the post referencing the URN (&lt;a href=&quot;https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/vector-asset-api&quot;&gt;LinkedIn: Vector asset upload&lt;/a&gt;). Note that the cited doc is LinkedIn’s legacy asset flow; the modern equivalent for posting an image is the Images API (&lt;code&gt;rest/images?action=initializeUpload&lt;/code&gt;), but the register/upload/reference shape is the same. &lt;code&gt;upload_binary&lt;/code&gt; takes a path here for brevity; a real client reads the file and PUTs the bytes. What matters for this post is the branch structure, not the wire details.&lt;/p&gt;
&lt;p&gt;The call site stays honest about this. The default invocation passes no image and takes the original branch; the image only enters after I have rendered and approved a PNG.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;png &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; attach_visual:                       &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# only after I approve the rendered image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    html &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; wrap_card(card_body)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    render_to_png(html, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.card&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;out/card.png&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    png &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;out/card.png&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;publish_post(draft_text, image_png&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;png)  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# png stays None on the trusted text-only path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Verify it&lt;/h2&gt;
&lt;p&gt;Two things are worth a regression test: that the text-only path never touches the image flow, and that the PNG really is high-DPI rather than nominally so. Both are cheap to assert and both fail loudly if a future change breaks them.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# tests/test_publish_and_render.py&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; unittest.mock &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; MagicMock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; PIL &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Image  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# test-only dependency, to read PNG dimensions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ghostwriter.publish &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; publish&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; ghostwriter.render &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; render_to_png&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_text_only_path_is_unchanged&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(monkeypatch):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    li &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; MagicMock()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    monkeypatch.setattr(publish, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;linkedin&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, li)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    publish.publish_post(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;hello world&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)               &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# no image argument&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    li.create_text_post.assert_called_once_with(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;hello world&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    li.register_image_upload.assert_not_called()       &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the image branch never runs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; test_png_is_high_dpi&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(tmp_path):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    out &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; tmp_path &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;card.png&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    html &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&amp;#x3C;div class=&apos;card&apos; style=&apos;width:200px;height:100px&apos;&gt;hi&amp;#x3C;/div&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    render_to_png(html, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.card&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(out), scale&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    width, height &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; Image.open(out).size&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    assert&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (width, height) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;400&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)               &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 2 device px per CSS px&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first test fails the moment text-only posts start calling the upload flow. The second fails if &lt;code&gt;device_scale_factor&lt;/code&gt; is dropped or set back to 1, which is exactly the silent regression that would make every image soft again without any error to notice.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;I’ll keep building card types as new post shapes call for them, and keep tightening the voice rules that govern the text. The bar for a new card type is that a post shape recurs often enough to deserve its own treatment, rather than adding a card because it looked good once.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://playwright.dev/python/docs/browsers&quot;&gt;Playwright: Installing browsers (Python)&lt;/a&gt; — install Chromium and its OS dependencies with &lt;code&gt;playwright install --with-deps&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://playwright.dev/python/docs/emulation&quot;&gt;Playwright: Emulation (Python)&lt;/a&gt; — set &lt;code&gt;device_scale_factor&lt;/code&gt; on the browser context to emulate a high-DPI display for crisp screenshots.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://playwright.dev/python/docs/api/class-locator&quot;&gt;Playwright: Locator API (Python)&lt;/a&gt; — screenshot a specific locator to crop output to a single element.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://playwright.dev/python/docs/api/class-page&quot;&gt;Playwright: Page API (Python)&lt;/a&gt; — &lt;code&gt;wait_until=&amp;quot;networkidle&amp;quot;&lt;/code&gt; is discouraged; wait on a rendered node instead.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mermaid.js.org/config/usage.html&quot;&gt;Mermaid: Usage&lt;/a&gt; — &lt;code&gt;mermaid.run()&lt;/code&gt; turns &lt;code&gt;&amp;lt;pre class=&amp;quot;mermaid&amp;quot;&amp;gt;&lt;/code&gt; source into an injected SVG.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/vector-asset-api&quot;&gt;LinkedIn: Vector asset upload&lt;/a&gt; — register an upload, PUT the image bytes, then create the post referencing the asset URN.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://12factor.net/config&quot;&gt;The Twelve-Factor App: Config&lt;/a&gt; — store config in the environment, separate from code; the open-source litmus test.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.gitguardian.com/secrets-api-management/&quot;&gt;GitGuardian: Secrets Management Best Practices&lt;/a&gt; — keep personal and sensitive values out of the repo; ship an example template instead.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle&quot;&gt;Wikipedia: Open–closed principle&lt;/a&gt; — add new behavior without modifying existing, working code paths.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[linkedin-ghostwriter] feat: optional diagrams &amp;amp; cards to accompany posts (&lt;a href=&quot;https://github.com/natejswenson/linkedin-ghostwriter/commit/d5c6e2b7a3ee81f31466dd3ae06edfe85bac6b95&quot;&gt;d5c6e2b&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[linkedin-ghostwriter] feat: per-user image brand guide with configurable byline (&lt;a href=&quot;https://github.com/natejswenson/linkedin-ghostwriter/commit/13fcfa9346e2309ba7e9c9b5675e303061723c41&quot;&gt;13fcfa9&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[linkedin-ghostwriter] feat: auto-open the rendered PNG so it’s actually viewed (&lt;a href=&quot;https://github.com/natejswenson/linkedin-ghostwriter/commit/d69e99a00fb2645bbc6c5519e84c928272d4f688&quot;&gt;d69e99a&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[linkedin-ghostwriter] feat: add a reusable date/deadline card type (&lt;a href=&quot;https://github.com/natejswenson/linkedin-ghostwriter/commit/c5b67942e04c597a58f0e56c0f3e74e6b8697faa&quot;&gt;c5b6794&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>A copy-paste component spreads its bugs: making the dev-log feed keyboard-accessible</title><link>https://natejswenson.com/devlog/devlog/v0.1.9/</link><guid isPermaLink="true">https://natejswenson.com/devlog/devlog/v0.1.9/</guid><description>The dev-log component shipped with a bare onClick toggle, so everyone who copied it inherited a feed keyboard and screen-reader users couldn&apos;t operate. Here is the full fix: the inaccessible before, the disclosure control that replaces it, the focus-visible CSS, and the keyboard test that keeps it honest.</description><pubDate>Fri, 05 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;The drop-in React component for rendering a dev log had an accessibility gap. Each entry’s expand/collapse was a bare &lt;code&gt;onClick&lt;/code&gt; on the &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; element, with no role, no &lt;code&gt;tabIndex&lt;/code&gt;, no &lt;code&gt;aria-expanded&lt;/code&gt;, and no keyboard handler. The README tells people to copy that component into their own site, so anyone who did inherited a feed that keyboard and screen-reader users could not operate. v0.1.9 reworks the toggle into a proper disclosure control on the entry header: &lt;code&gt;role=&amp;quot;button&amp;quot;&lt;/code&gt; with &lt;code&gt;tabIndex&lt;/code&gt;, &lt;code&gt;aria-expanded&lt;/code&gt; and &lt;code&gt;aria-controls&lt;/code&gt; for screen-reader semantics, an Enter/Space handler that prevents Space’s default so the page doesn’t scroll, and a &lt;code&gt;:focus-visible&lt;/code&gt; outline so keyboard focus is visible. The visuals are unchanged; padding, hover, and cursor just moved from the entry to the header.&lt;/p&gt;
&lt;p&gt;Moving the control to the header instead of wrapping the whole card also fixed two quieter problems: the expanded body’s links are no longer trapped inside an interactive ancestor, and normal text selection works again. The part worth writing about is that this was never one site’s bug. A component meant to be copied carries its defects into every copy, so this walks the full fix end to end: the inaccessible starting point, the accessible disclosure that replaces it, the CSS that makes focus visible, the call site, and a test that proves the keyboard path works.&lt;/p&gt;
&lt;h2&gt;The bug: a bare onClick is not a button&lt;/h2&gt;
&lt;p&gt;Here is the shape of what shipped before. It looks fine, because for a mouse user it is fine.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// BEFORE: works for a mouse, invisible to everything else.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Entry&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;({ title, body, expanded, onToggle }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; onClick={&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;onToggle&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      {&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;expanded &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; className=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;entry-body&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two things are broken, and neither shows up in a quick click-test. First, &lt;code&gt;onClick&lt;/code&gt; on a non-interactive element only fires for the pointer. The &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; is not in the tab order, so a keyboard user can never reach it, which fails the requirement that all functionality be operable from a keyboard (&lt;a href=&quot;https://www.w3.org/WAI/WCAG21/Understanding/keyboard.html&quot;&gt;W3C: Understanding SC 2.1.1 Keyboard&lt;/a&gt;). Second, the element carries no interactive role and no announced state, so a screen reader reads a block of text with no hint that it does anything or whether it is open. The &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; does expose an implicit &lt;code&gt;article&lt;/code&gt; role, but that describes a document section, not a control, and it conveys nothing about expanded or collapsed. That is exactly the gap the Name, Role, Value criterion exists to close (&lt;a href=&quot;https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html&quot;&gt;W3C: Understanding SC 4.1.2 Name, Role, Value&lt;/a&gt;). A custom control built from a generic element has no button role and no announced state unless you add them.&lt;/p&gt;
&lt;h2&gt;Build the disclosure header&lt;/h2&gt;
&lt;p&gt;A show/hide toggle is a named, specified widget: the disclosure pattern. The WAI-ARIA Authoring Practices Guide defines it as a button that controls the visibility of a section of content, carrying &lt;code&gt;aria-expanded&lt;/code&gt; (true when the content is visible, false when hidden) and optionally &lt;code&gt;aria-controls&lt;/code&gt; pointing at the controlled region, activated by both Enter and Space (&lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/&quot;&gt;W3C APG: Disclosure Pattern&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Because this is a custom control rather than a native &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, every piece has to be added by hand. MDN’s checklist for &lt;code&gt;role=&amp;quot;button&amp;quot;&lt;/code&gt; is the spec to follow: make the element focusable with &lt;code&gt;tabIndex&lt;/code&gt;, handle &lt;code&gt;click&lt;/code&gt; for the pointer, and add a &lt;code&gt;keydown&lt;/code&gt; handler for Enter and Space, because the click event will not fire for the keyboard. The easy-to-miss detail is that Space scrolls the page by default, so the handler has to call &lt;code&gt;preventDefault()&lt;/code&gt; for Space (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/button_role&quot;&gt;MDN: ARIA button role&lt;/a&gt;).&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// EntryHeader.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { KeyboardEvent } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;react&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Props&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  title&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  expanded&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; boolean&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  onToggle&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; EntryHeader&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;({ id, title, expanded, onToggle }&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Props&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; handleKeyDown&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(e&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; KeyboardEvent&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (e.key &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;Enter&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; e.key &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      e.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;preventDefault&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;();   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// Space scrolls the page by default; stop that first&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;      onToggle&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      role=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;button&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;                 // a generic element needs an explicit role&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      tabIndex={&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;                  // ...and must be put in the tab order&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      aria-expanded={&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;expanded&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;      // announces open/closed state to screen readers&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      aria-controls={&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`body-${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // points at the region this toggles&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      onClick={&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;onToggle&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;            // pointer activation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      onKeyDown={&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;handleKeyDown&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;     // keyboard activation; click won&apos;t fire for keys&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      className=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;entry-header&quot;&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;      // :focus-visible outline lives here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      {&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One honest caveat: a native &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; gives you focusability, the role, and Enter/Space activation for free, and both the APG and MDN recommend reaching for it first. The custom-element version is what you use when the clickable region cannot be a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, and the cost of that choice is that you now own every behavior the browser would otherwise have handled.&lt;/p&gt;
&lt;h2&gt;Make keyboard focus visible&lt;/h2&gt;
&lt;p&gt;Adding &lt;code&gt;tabIndex&lt;/code&gt; puts the header in the tab order, but a keyboard user also has to see where focus is, and many resets strip the default outline. &lt;code&gt;:focus-visible&lt;/code&gt; is the right selector here: it shows a ring for keyboard focus without painting one on every mouse click. The controlled region carries the matching &lt;code&gt;id&lt;/code&gt; that &lt;code&gt;aria-controls&lt;/code&gt; points at.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* entry.css */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.entry-header&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  cursor&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; pointer&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  padding&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0.75rem&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1rem&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.entry-header&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:hover&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  background&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #f4f4f5&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;/* Visible ring for keyboard focus, quiet for the mouse. */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;.entry-header&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:focus-visible&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  outline&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 2px&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; solid&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; #2563eb&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  outline-offset&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 2px&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use it in the feed&lt;/h2&gt;
&lt;p&gt;The header is one entry’s control; the feed renders a list of them and owns which entries are open. Each &lt;code&gt;aria-controls&lt;/code&gt; target gets the matching &lt;code&gt;id&lt;/code&gt;, so the toggle and its body are wired together for assistive tech.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// DevLogFeed.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { useState } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;react&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { EntryHeader } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;./EntryHeader&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Entry&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; title&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; body&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; DevLogFeed&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;({ entries }&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { entries&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Entry&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;[] }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [openId, setOpenId] &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;&gt;(null);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; className=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;devlog-feed&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      {&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;entries.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;((entry) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; expanded &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; entry.id &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; openId;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;          &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; key={&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;entry.id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;EntryHeader&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;              id={&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;entry.id&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;              title={&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;entry.title&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;              expanded={&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;expanded&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;              onToggle={&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; setOpenId&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(expanded &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; null &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; entry.id)&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; id={&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`body-${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; hidden={!&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;expanded&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; className=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;entry-body&quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;              {&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;entry.body&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;            &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;          &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;      })&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The body uses &lt;code&gt;hidden&lt;/code&gt; rather than unmounting, so &lt;code&gt;aria-controls&lt;/code&gt; always references a real element and a screen reader can find the region it points at.&lt;/p&gt;
&lt;h2&gt;Verify with a keyboard test&lt;/h2&gt;
&lt;p&gt;The two behaviors worth defending are that the keyboard activates the toggle and that the state is announced. Testing Library is built for this; it queries by role and simulates real keyboard events, so the test exercises the same path a keyboard user takes (&lt;a href=&quot;https://testing-library.com/docs/user-event/keyboard/&quot;&gt;Testing Library: user-event keyboard&lt;/a&gt;). Both Enter and Space must toggle &lt;code&gt;aria-expanded&lt;/code&gt; and show or hide the body.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// DevLogFeed.test.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;@testing-library/jest-dom&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;   &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// registers toBeVisible / toHaveAttribute matchers&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { render, screen } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;@testing-library/react&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; userEvent &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;@testing-library/user-event&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { DevLogFeed } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;./DevLogFeed&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; entries &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; [{ id: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, title: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;First post&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, body: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Body text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; }];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Enter and Space toggle the disclosure&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; user &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; userEvent.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;setup&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  render&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;DevLogFeed&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; entries={&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;entries&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; /&gt;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; toggle &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; screen.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;getByRole&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;button&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, { name: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;First post&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  expect&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(toggle).&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;toHaveAttribute&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;aria-expanded&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  expect&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(screen.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;queryByText&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Body text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)).not.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;toBeVisible&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  toggle.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;focus&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;();                                  &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// reach it the way a keyboard user does&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  await&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; user.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;keyboard&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;{Enter}&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  expect&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(toggle).&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;toHaveAttribute&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;aria-expanded&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  expect&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(screen.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;getByText&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Body text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)).&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;toBeVisible&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  await&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; user.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;keyboard&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);                         &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// Space collapses it again&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  expect&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(toggle).&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;toHaveAttribute&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;aria-expanded&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  expect&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(screen.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;queryByText&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Body text&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;)).not.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;toBeVisible&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;getByRole(&amp;quot;button&amp;quot;, ...)&lt;/code&gt; is itself part of the assertion: it only finds the element because the role is now exposed, so the test fails against the bare-&lt;code&gt;onClick&lt;/code&gt; version before it ever presses a key. Run it against the old &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; and the query throws; run it against the disclosure and the keyboard path is proven.&lt;/p&gt;
&lt;h2&gt;Why this earned its own release&lt;/h2&gt;
&lt;p&gt;The reason a keyboard fix was worth cutting a version for is the distribution model. This component isn’t an internal page; the README points people to copy it into their own sites. A reusable artifact is a multiplier, and it multiplies whatever it contains, including its bugs. One inaccessible toggle in a template becomes an inaccessible toggle on every site that adopts it, and most adopters will never audit the keyboard path; they trust that a published component does the obvious thing right. The flip side is the encouraging part: fixing the template fixes every future copy by default, so anyone pulling 0.1.9 now gets the accessible version without doing anything. The standard for a shared component is higher than “works for me”; it’s “the accessible behavior is the one you get when you copy it and change nothing,” because the default is what actually propagates.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;The next pass sweeps the rest of the example app for the same class of gap, then folds this disclosure pattern into the docs as the documented default, so the accessible version is the one people start from rather than the one they have to discover.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/&quot;&gt;W3C APG: Disclosure (Show/Hide) Pattern&lt;/a&gt; — the specified disclosure widget: a button with &lt;code&gt;aria-expanded&lt;/code&gt;/&lt;code&gt;aria-controls&lt;/code&gt;, activated by both Enter and Space.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/button_role&quot;&gt;MDN: ARIA button role&lt;/a&gt; — a custom button needs &lt;code&gt;tabIndex&lt;/code&gt;, click and keydown handlers for Enter/Space, and &lt;code&gt;preventDefault()&lt;/code&gt; on Space; prefer a native &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; when you can.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/WAI/WCAG21/Understanding/keyboard.html&quot;&gt;W3C: Understanding SC 2.1.1 Keyboard&lt;/a&gt; — all functionality must be operable through a keyboard interface.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html&quot;&gt;W3C: Understanding SC 4.1.2 Name, Role, Value&lt;/a&gt; — custom controls must expose their role, state, and value to assistive technology.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://testing-library.com/docs/user-event/keyboard/&quot;&gt;Testing Library: user-event keyboard&lt;/a&gt; — simulating real keyboard interaction so a test exercises the same path a keyboard user takes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[devlog] fix(a11y): make dev-log entries keyboard- and screen-reader-accessible (&lt;a href=&quot;https://github.com/natejswenson/devlog/commit/24a60a1177597eaaa69bd988785cdb4aaaafccab&quot;&gt;24a60a1&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[devlog] chore: release 0.1.9 (&lt;a href=&quot;https://github.com/natejswenson/devlog/commit/b8edbfb5ba20f7f3446a342cc54d62b0067fa65d&quot;&gt;b8edbfb&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Turning a personal tool into a package people can install</title><link>https://natejswenson.com/devlog/devlog/2026-05-01/</link><guid isPermaLink="true">https://natejswenson.com/devlog/devlog/2026-05-01/</guid><description>I shipped my devlog skill to npm as `npx @natjswenson/devlog`. The work that mattered wasn&apos;t the feature; it was pulling my own identity out of the code, wiring up an npx CLI with an init command, and reading npm&apos;s error codes correctly when publishing went sideways.</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Shipped&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;/devlog&lt;/code&gt; skill has been running on &lt;a href=&quot;http://natejswenson.io&quot;&gt;natejswenson.io&lt;/a&gt; for weeks: it reads git commits and writes narrative dev-log entries. After a few people asked about it on LinkedIn, I genericized it, wrapped it in an npx CLI, and published it to npm so anyone can install it with one command. &lt;code&gt;npx @natjswenson/devlog init&lt;/code&gt; now auto-fills a few defaults from &lt;code&gt;gh&lt;/code&gt; and &lt;code&gt;git config&lt;/code&gt;, creates the user’s daily-dev-log repo with &lt;code&gt;gh repo create&lt;/code&gt;, installs the skill, and writes a config file. A &lt;code&gt;preview&lt;/code&gt; command spins up a local Vite app pointed at the user’s published log, and the bundled demo renders the real component against canned data when there’s nothing real to show yet.&lt;/p&gt;
&lt;p&gt;The skill itself was the easy part. It already worked. The part worth writing about is what it takes to turn a tool that’s hardcoded to one person into something a stranger can adopt without forking it: pulling the user’s identity out into config, declaring the executable so &lt;code&gt;npx&lt;/code&gt; can find it, scaffolding setup with an &lt;code&gt;init&lt;/code&gt; command, and then verifying the whole thing resolves before you trust a publish. Here is the end-to-end build.&lt;/p&gt;
&lt;h2&gt;Setup: pull the user out of the code&lt;/h2&gt;
&lt;p&gt;The original skill was hardcoded to me in three places: “Nate” as the commit-author filter, my project list written inline in &lt;code&gt;SKILL.md&lt;/code&gt;, and my dev-log repo as the push target. Sharing it as-is would have meant writing a paragraph of “replace this, then this, then this,” and then watching every adopter hit a merge conflict the next time I updated the upstream skill, because their edits and my edits would land in the same file.&lt;/p&gt;
&lt;p&gt;So I moved all of that out of the code and into a single &lt;code&gt;config.json&lt;/code&gt; at &lt;code&gt;~/.claude/skills/devlog/&lt;/code&gt;, outside the skill directory entirely. This is the third of the twelve factors, store config separate from code; the methodology’s own litmus test is whether you could open-source the codebase at any moment without leaking anything deploy-specific (&lt;a href=&quot;https://12factor.net/config&quot;&gt;The Twelve-Factor App: Config&lt;/a&gt;). Start with the shape of the config the skill reads.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;author&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;Nate Swenson&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;githubUser&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;natejswenson&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;logRepo&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;natejswenson/daily-dev-log&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;projects&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;natejswenson.io&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;local-fitness&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;ghostwriter&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The skill becomes config-driven: instead of baking in assumptions, it reads these values, and falls back to a clear error when the file is missing rather than silently using my identity.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// skill/config.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { readFile } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;node:fs/promises&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { homedir } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;node:os&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { join } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;node:path&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; CONFIG_PATH &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; join&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;homedir&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.claude&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;skills&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;devlog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;config.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; loadConfig&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  try&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; JSON.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; readFile&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(CONFIG_PATH, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (err) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (err.code &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;ENOENT&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;      throw&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`No config at ${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;CONFIG_PATH&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}. Run &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\`&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;npx @natjswenson/devlog init&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\`&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; first.`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    throw&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; err;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the user’s identity lives in a file the skill merely reads, the open-source test passes, and the practical payoff shows up too: adopters never touch the shipped files, so they can pull updates cleanly forever.&lt;/p&gt;
&lt;h2&gt;Build: declare the executable with bin and a shebang&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;npx&lt;/code&gt; is what makes “install with one command” true. It runs a package’s executable without a permanent global install, fetching it on demand if it isn’t already present (&lt;a href=&quot;https://docs.npmjs.com/cli/v11/commands/npx/&quot;&gt;npm Docs: npx&lt;/a&gt;). For that to work, the package has to declare its executable in the &lt;code&gt;bin&lt;/code&gt; field of &lt;code&gt;package.json&lt;/code&gt;, which maps a command name to the file npm puts on the user’s PATH (&lt;a href=&quot;https://docs.npmjs.com/cli/v11/configuring-npm/package-json/&quot;&gt;npm Docs: package.json bin&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;My first publish surfaced a warning here. I’d written the bin path with a leading &lt;code&gt;./&lt;/code&gt;, the way you’d reference a relative file most other places. npm warns and auto-cleans the leading &lt;code&gt;./&lt;/code&gt; (suggesting &lt;code&gt;npm pkg fix&lt;/code&gt;); the publish still succeeds and the command still works, but writing the path bare avoids the warning.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;@natjswenson/devlog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;version&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;module&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;bin&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    &quot;devlog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;bin/cli.js&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  &quot;files&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;bin&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;skill&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file &lt;code&gt;bin&lt;/code&gt; points at must start with a &lt;code&gt;#!/usr/bin/env node&lt;/code&gt; shebang, or npm will try to run it without the Node runtime. That line tells the OS to execute the script with whatever &lt;code&gt;node&lt;/code&gt; is on PATH, which is the portable form across machines.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;#!/usr/bin/env node&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// bin/cli.js: the shebang above is mandatory, or this runs without Node.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; command &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; process.argv[&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (command &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;init&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { runInit } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;../skill/init.js&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  await&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; runInit&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; (command &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;preview&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { runPreview } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;../skill/preview.js&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  await&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; runPreview&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;usage: devlog &amp;#x3C;init|preview&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  process.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;exit&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(command &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each branch loads its handler lazily, so &lt;code&gt;init&lt;/code&gt; never imports the preview machinery and vice versa. The &lt;code&gt;preview&lt;/code&gt; handler is small on purpose; it just boots the bundled Vite app against the user’s published log. That &lt;code&gt;preview-app&lt;/code&gt; is an ordinary Vite project shipped inside the package, so I’m leaving its scaffolding out of scope here.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// skill/preview.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { spawn } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;node:child_process&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { dirname, join } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;node:path&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { fileURLToPath } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;node:url&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; runPreview&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // Boot the bundled Vite preview app; it reads the same config.json init wrote.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; appDir &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; join&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;dirname&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;fileURLToPath&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.meta.url)), &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;preview-app&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  spawn&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;npx&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;vite&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, appDir], { stdio: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;inherit&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the bin declared with a bare path, &lt;code&gt;npx @natjswenson/devlog init&lt;/code&gt; resolves the &lt;code&gt;devlog&lt;/code&gt; command and runs the CLI, no global install required.&lt;/p&gt;
&lt;h2&gt;Build: an init command with sensible defaults&lt;/h2&gt;
&lt;p&gt;The point of &lt;code&gt;init&lt;/code&gt; is that an adopter shouldn’t have to know what goes in &lt;code&gt;config.json&lt;/code&gt;. The CLI should fill in everything it can read from the tools they already have. &lt;code&gt;git config user.name&lt;/code&gt; gives the author, and the GitHub CLI’s &lt;code&gt;gh api user&lt;/code&gt; gives the account login, so the only thing left to ask is which repo to push logs to, and even that gets a default.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// skill/init.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { execFileSync } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;node:child_process&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { writeFile, mkdir, cp } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;node:fs/promises&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { homedir } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;node:os&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { join, dirname } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;node:path&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { fileURLToPath } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;node:url&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; SKILL_DIR &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; join&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;homedir&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.claude&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;skills&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;devlog&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; CONFIG_PATH &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; join&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(SKILL_DIR, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;config.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; tryCmd&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(file, args) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  try&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; execFileSync&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(file, args, { encoding: &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; }).&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;trim&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  } &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;// tool missing or not logged in; fall back to a default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; runInit&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; author &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; tryCmd&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;config&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user.name&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;Your Name&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; githubUser &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; tryCmd&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;gh&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;api&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;--jq&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;.login&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;your-handle&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; logRepo &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; `${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;githubUser&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}/daily-dev-log`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; config &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; { author, githubUser, logRepo, projects: [] };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  await&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; mkdir&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(SKILL_DIR, { recursive: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  await&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; writeFile&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(CONFIG_PATH, JSON.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(config, null, &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`Wrote ${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;CONFIG_PATH&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // Create the log repo (no-op if it already exists or gh is missing).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;  tryCmd&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;gh&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, [&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;repo&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;create&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, logRepo, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;--public&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;--clone=false&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`Created repo ${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;logRepo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // Copy the bundled skill next to the config so the agent can load it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;  // Exclude config.json so a shipped one can&apos;t clobber what we just wrote.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt; skillSrc &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; dirname&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;fileURLToPath&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;.meta.url));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;  await&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; cp&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(skillSrc, SKILL_DIR, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;    recursive: &lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;    filter&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;: (src) &lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#A0A0A0&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;src.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;endsWith&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;&quot;config.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;`Installed skill to ${&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;SKILL_DIR&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#FFF&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Defaults that come from the environment are the difference between a setup an adopter completes and one they abandon. The values are pre-filled and correct for the common case, and they only have to override the one that’s wrong.&lt;/p&gt;
&lt;h2&gt;Use it&lt;/h2&gt;
&lt;p&gt;For a new adopter, the whole setup is one command. &lt;code&gt;npx&lt;/code&gt; fetches the package, finds the &lt;code&gt;devlog&lt;/code&gt; bin, and runs the &lt;code&gt;init&lt;/code&gt; branch.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; npx&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; @natjswenson/devlog&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; init&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;Wrote&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; /Users/you/.claude/skills/devlog/config.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;Created&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; repo&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; your-handle/daily-dev-log&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;Installed&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; skill&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; /Users/you/.claude/skills/devlog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# later, see it rendered locally against your published log:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; npx&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; @natjswenson/devlog&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; preview&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No clone, no fork, no editing shipped files. The skill reads the config that &lt;code&gt;init&lt;/code&gt; just wrote, and updates to the package land cleanly because the user’s data was never in the package to begin with.&lt;/p&gt;
&lt;h2&gt;Verify: confirm the bin resolves before you publish&lt;/h2&gt;
&lt;p&gt;Before publishing, you want proof that the executable is actually in the tarball and that it resolves to a runnable command. &lt;code&gt;npm pack&lt;/code&gt; builds the exact tarball npm would publish and prints its contents, so you can confirm &lt;code&gt;bin/cli.js&lt;/code&gt; is included; &lt;code&gt;npm link&lt;/code&gt; then puts the bin on your PATH locally so you can run it by name.&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 1. See exactly what would publish; bin/cli.js must be listed.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; npm&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; pack&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --dry-run&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# 2. Symlink the package globally and confirm the command resolves.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; npm&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; which&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; devlog&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;          # -&gt; .../bin/devlog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; devlog&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;                # -&gt; usage: devlog &amp;#x3C;init|preview&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second publish failed even after the bin was correct, this time with a 404, and the instinct with a publish failure is to reach for a 403, a permissions problem. A 404 is different: it means the thing you addressed doesn’t exist. The clue was the scope. I tried to publish under &lt;code&gt;@natejswenson&lt;/code&gt;, but my actual npm username is &lt;code&gt;natjswenson&lt;/code&gt;, missing the “e”, a registration typo I made years ago and never noticed. npm scopes are tied to a username or org exactly, so &lt;code&gt;@natejswenson&lt;/code&gt; returned “scope not found,” which the registry reports as a 404 rather than a 403 (&lt;a href=&quot;https://docs.npmjs.com/cli/v11/using-npm/scope&quot;&gt;npm Docs: scope&lt;/a&gt;). HTTP status codes carry that distinction on purpose: 403 is “the request was understood but refused,” 404 is “I have no representation for this resource” (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status&quot;&gt;MDN: HTTP response status codes&lt;/a&gt;).&lt;/p&gt;
&lt;pre class=&quot;shiki vesper&quot; style=&quot;color:#FFF&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Publishing under a scope you don&apos;t own returns 404, not 403.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; npm&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; publish&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --access&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; public&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; error&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; code&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; E404&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; error&lt;/span&gt;&lt;span style=&quot;color:#FFC799&quot;&gt; 404&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; Not&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; Found&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; PUT&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; https://registry.npmjs.org/@natejswenson%2fdevlog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# the 404 body is the registry&apos;s way of saying the scope @natejswenson doesn&apos;t exist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;# Fix: rename to the scope you actually own, then it ships.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; npm&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; whoami&lt;/span&gt;&lt;span style=&quot;color:#8B8B8B94&quot;&gt;            # natjswenson  (no &quot;e&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFC799&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; npm&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; publish&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; --access&lt;/span&gt;&lt;span style=&quot;color:#99FFE4&quot;&gt; public&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lesson that generalizes: when a tool hands you a status code, trust what that specific code means instead of the failure you were braced for.&lt;/p&gt;
&lt;h2&gt;Next&lt;/h2&gt;
&lt;p&gt;Next is linking it on the LinkedIn thread that started this. The first feedback will probably be requests for non-React framework examples, Astro and Next, since the lifted preview app is React-only, plus Windows path handling in &lt;code&gt;init&lt;/code&gt;, which currently assumes a Unix-style home directory.&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://12factor.net/config&quot;&gt;The Twelve-Factor App: Config&lt;/a&gt; — store config separate from code so the codebase could be open-sourced without leaking deploy-specific values.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.npmjs.com/cli/v11/commands/npx/&quot;&gt;npm Docs: npx&lt;/a&gt; — runs a package’s executable on demand without a permanent global install.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.npmjs.com/cli/v11/configuring-npm/package-json/&quot;&gt;npm Docs: package.json &lt;code&gt;bin&lt;/code&gt;&lt;/a&gt; — maps a command name to an executable file; that file needs a &lt;code&gt;#!/usr/bin/env node&lt;/code&gt; shebang.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.npmjs.com/cli/v11/using-npm/scope&quot;&gt;npm Docs: scope&lt;/a&gt; — a scope is tied to a username or org; publishing under one you don’t own fails.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status&quot;&gt;MDN: HTTP response status codes&lt;/a&gt; — 403 means refused, 404 means the resource doesn’t exist.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[devlog] docs: add design doc for shareable @natjswenson/devlog repo (&lt;a href=&quot;https://github.com/natejswenson/devlog/commit/5472afb4a6d281b09ceeb9fd626ee4e24b03d962&quot;&gt;5472afb&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[devlog] feat: shareable @natjswenson/devlog package (&lt;a href=&quot;https://github.com/natejswenson/devlog/commit/819815d9d8e0bf2775f845c0e7a350df4e276259&quot;&gt;819815d&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[devlog] feat: snarky demo mode for preview app + funnier placeholder configs (&lt;a href=&quot;https://github.com/natejswenson/devlog/commit/3d17bb9be2ab22f71b84b4726b409a28800db49e&quot;&gt;3d17bb9&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[devlog] fix: drop leading ./ from bin path so npm publish keeps the entry (&lt;a href=&quot;https://github.com/natejswenson/devlog/commit/0804d53c8b25122b49a7a67d428f805e85eface9&quot;&gt;0804d53&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[devlog] fix: rename package to @natjswenson/devlog to match npm scope (&lt;a href=&quot;https://github.com/natejswenson/devlog/commit/26e755227b344193ec092bbdf7b8463a48a8ca18&quot;&gt;26e7552&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item></channel></rss>