<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Smoke Some Paint]]></title><description><![CDATA[Things I think about.]]></description><link>https://www.smokesomepaint.com</link><image><url>https://substackcdn.com/image/fetch/$s_!JbpN!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa12244d0-7271-4573-8a28-917e74ac5650_1024x1024.png</url><title>Smoke Some Paint</title><link>https://www.smokesomepaint.com</link></image><generator>Substack</generator><lastBuildDate>Tue, 26 May 2026 08:30:35 GMT</lastBuildDate><atom:link href="https://www.smokesomepaint.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Cully Wakelin]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[smokesomepaint@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[smokesomepaint@substack.com]]></itunes:email><itunes:name><![CDATA[Cully Wakelin]]></itunes:name></itunes:owner><itunes:author><![CDATA[Cully Wakelin]]></itunes:author><googleplay:owner><![CDATA[smokesomepaint@substack.com]]></googleplay:owner><googleplay:email><![CDATA[smokesomepaint@substack.com]]></googleplay:email><googleplay:author><![CDATA[Cully Wakelin]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Turning AI-generated brand marks into production SVGs]]></title><description><![CDATA[For the cost of about an hour of focused work and a couple of dollars in API spend, the pipeline produces a working brand-mark family.]]></description><link>https://www.smokesomepaint.com/p/turning-ai-generated-brand-marks</link><guid isPermaLink="false">https://www.smokesomepaint.com/p/turning-ai-generated-brand-marks</guid><dc:creator><![CDATA[Cully Wakelin]]></dc:creator><pubDate>Tue, 19 May 2026 19:41:25 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!PR8P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PR8P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PR8P!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!PR8P!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!PR8P!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!PR8P!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PR8P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PR8P!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!PR8P!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!PR8P!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!PR8P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14053f4c-40b7-485d-8482-aadc01a32669_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>AI image models are good enough now to land a brand mark in just a few minutes. The mark comes out as a raster PNG &#8212; beautiful and locked, but not yet in a shape you can actually ship.</p><p>A production identity needs vectors. It needs surface-explicit variants &#8212; on-light, on-dark, mono-black, mono-white &#8212; for the surfaces a brand actually lives on. It needs a favicon set with raster fallbacks for the browsers and OS-es that won&#8217;t render SVG at the favicon slot. It needs fills at the exact brand hex values, not the slightly-off versions a neural tracer hands you. And it needs a file size you can put on a web page without apologizing for it.</p><p>The path from raster to that production family looks short on paper. In practice it&#8217;s full of small mistakes that each cost some API credits and an hour of fumbling to figure out. The point of writing the pipeline down is that the second person to walk it shouldn&#8217;t have to learn the same mistakes the first one did.</p><p>For the cost of about an hour and $1.50&#8211;$2.50 in API spend, the following pipeline produces a complete primary-mark family: a primary on-light variant, a primary on-dark variant, mono-black, mono-white, a sigil, and the favicon set. Most of that hour is QC and variant generation rather than the API calls themselves.</p><h2>When this pipeline applies</h2><p>The starting state assumed here:</p><ul><li><p>A locked AI-generated primary mark as a raster PNG (gpt-image-2, Flux, Midjourney, whatever)</p></li><li><p>A locked brand palette of 3&#8211;5 hex values</p></li><li><p>An expectation of shipping production SVGs to a brand asset library</p></li></ul><p>The pipeline does NOT apply if:</p><ul><li><p>The mark is destined for billboard-scale signage or embroidery &#8212; those need a designer cleanup pass in Illustrator on top of this output</p></li><li><p>The mark is silhouette-only &#8212; Potrace handles those in one step, no neural tracer needed</p></li><li><p>The mark only ever lives on one surface &#8212; just chroma-key a transparent PNG and skip vectoring</p></li></ul><h2>Tools</h2><p>All command-line, all standard:</p><ul><li><p><code>magick</code> (ImageMagick 7+) &#8212; flattening, chroma-key, color quantization</p></li><li><p><code>rsvg-convert</code> &#8212; SVG &#8594; PNG render for QC</p></li><li><p><code>svgo</code> &#8212; multipass SVG optimization (Node global)</p></li><li><p><code>python3</code> for a short palette-snapping script (standard lib only)</p></li><li><p><code>curl</code> &#8212; for the Vectorizer.AI API</p></li></ul><h2>Subscriptions</h2><p>The pipeline uses the Vectorizer.AI API. The cheapest API tier is around $13 CAD/month for 50 credits.</p><p>One trap worth flagging up front: Vectorizer.AI sells web-app and API subscriptions as <strong>separate SKUs at the same price</strong>. The web-app subscription gives unlimited browser uploads and <strong>zero API credits</strong>. Subscribing to the wrong one and then watching every API call 402 is a special kind of frustrating. Verify with the account endpoint before assuming API calls work:</p><pre><code><code>curl -sS https://vectorizer.ai/api/v1/account \
  -u "$VECTORIZER_AI_APP_ID:$VECTORIZER_AI_API_SECRET"</code></code></pre><p>The API plan name looks like <code>vec_999m_50</code> (50 credits) with a non-zero <code>credits</code> value. The web-app plan name is <code>unlimited_999m</code>. If you see the second one, the API will not work no matter how many calls you make.</p><h2>The pipeline</h2><p>Eight steps, in order. A couple of them depend on what the earlier ones produced, so the order isn&#8217;t optional even if it looks like it.</p><h3>Step 1 &#8212; Verify the source PNG has a solid background</h3><p>This is one of the most expensive mistakes in the pipeline, and also one of the easiest to avoid. Vectorizer.AI&#8217;s neural tracer treats anti-aliased alpha-edge pixels as a distinct color category, and it emits a separate path for every band of partial transparency. If you feed it a chroma-keyed transparent PNG, the output will be 5,000+ paths and a 900 KB SVG that isn&#8217;t really shippable.</p><pre><code><code>file path/to/source.png
# Want: PNG image data, 1024 x 1024, 8-bit/color RGB, non-interlaced
# AVOID: 8-bit/color RGBA  (the A = transparency)</code></code></pre><p>If the source is RGBA, re-flatten against a solid color before tracing:</p><pre><code><code>magick source.png -background "#1A0E0F" -flatten flat.png</code></code></pre><p>The flatten color doesn&#8217;t have to be a brand color. Vectorizer.AI will trace it as one of the output colors, and you can either drop it from the SVG after or accept it as the &#8220;on this surface&#8221; variant.</p><h3>Step 2 &#8212; Vectorize via the Vectorizer.AI API</h3><pre><code><code>curl -sS https://vectorizer.ai/api/v1/vectorize \
  -u "$VECTORIZER_AI_APP_ID:$VECTORIZER_AI_API_SECRET" \
  -F image=@source.png \
  -F mode=production \
  -F output.file_format=svg \
  -F processing.max_colors=5 \
  -F output.svg.version=svg_1_1 \
  -F output.svg.fixed_size=true \
  -o raw-output.svg \
  -w "HTTP %{http_code} | %{size_download}B | %{time_total}s\n"</code></code></pre><p>A few notes on those parameters:</p><ul><li><p><code>mode=production</code> &#8212; 1.0 credits, no watermark, unrestricted output. <code>mode=test</code> is free but embeds a 2,600-path watermark mesh that ruins visual QC. Test mode is only useful to confirm auth.</p></li><li><p><code>processing.max_colors</code> &#8212; set to <strong>one more than your palette size</strong>. For a 4-color palette set <code>max_colors=5</code>. Setting it equal to palette size will quantize the least-frequent color out. For pure mono sources use <code>max_colors=2</code>.</p></li><li><p><code>output.svg.fixed_size=true</code> &#8212; gives you a viewBox-locked SVG that scales predictably.</p></li><li><p><code>output.svg.version=svg_1_1</code> &#8212; broader compatibility than svg_2_0, which matters for email clients and print pipelines.</p></li></ul><p>Expect 70&#8211;130 KB output, 80&#8211;150 paths, 8&#8211;12 seconds per call. If you see 500+ KB or 1000+ paths, the source was probably transparent or has noisy edges. Re-flatten and try again.</p><h3>Step 3 &#8212; Palette-snap every fill to exact brand hex</h3><p>Vectorizer.AI&#8217;s output colors drift slightly from the source. A pure <code>#FF2D87</code> accent will come back as <code>#fc1e6a</code>, plus a handful of near-cream variants from anti-aliased edges. LAB-distance snapping cleans this up deterministically.</p><pre><code><code>"""palette-snap.py &lt;input.svg&gt; &lt;output.svg&gt;
Snaps every fill/stroke hex value to the nearest brand-palette color in LAB."""

import re, sys
from pathlib import Path

PALETTE = {
    # Edit per brand
    "#1A0E0F": "ink",
    "#F5E9D3": "paper",
    "#FF2D87": "accent1",
    "#8B1A2B": "accent2",
    "#FFFFFF": "white",
}

def hex_to_rgb(s):
    h = s.lstrip("#")
    if len(h) == 3: h = "".join(c*2 for c in h)
    return int(h[0:2],16), int(h[2:4],16), int(h[4:6],16)

def rgb_to_lab(rgb):
    r,g,b = (c/255 for c in rgb)
    def linear(c): return c/12.92 if c &lt;= 0.04045 else ((c+0.055)/1.055)**2.4
    r,g,b = linear(r), linear(g), linear(b)
    x = (r*0.4124564 + g*0.3575761 + b*0.1804375) / 0.95047
    y = (r*0.2126729 + g*0.7151522 + b*0.0721750)
    z = (r*0.0193339 + g*0.1191920 + b*0.9503041) / 1.08883
    def f(t): return t**(1/3) if t &gt; 0.008856 else (7.787*t + 16/116)
    fx, fy, fz = f(x), f(y), f(z)
    return (116*fy - 16, 500*(fx-fy), 200*(fy-fz))

PALETTE_LAB = {h: rgb_to_lab(hex_to_rgb(h)) for h in PALETTE}

def nearest(hex_str):
    lab = rgb_to_lab(hex_to_rgb(hex_str))
    return min(PALETTE_LAB, key=lambda h: sum((a-b)**2 for a,b in zip(lab, PALETTE_LAB[h])))

HEX_RE = re.compile(r'#[0-9a-fA-F]{6}\b|#[0-9a-fA-F]{3}\b')
src, dst = Path(sys.argv[1]), Path(sys.argv[2])
content = src.read_text()
stats = {}
def replace(m):
    orig = m.group(0).upper()
    if len(orig) == 4: orig = "#" + "".join(c*2 for c in orig[1:])
    snapped = nearest(orig)
    stats[f"{orig} -&gt; {snapped}"] = stats.get(f"{orig} -&gt; {snapped}", 0) + 1
    return snapped
dst.write_text(HEX_RE.sub(replace, content))
for k, v in sorted(stats.items(), key=lambda kv: -kv[1]):
    print(f"  {v:5d}  {k}")</code></code></pre><p>Run it:</p><pre><code><code>python3 palette-snap.py raw-output.svg snapped.svg</code></code></pre><p>The script prints what it snapped to what. Eyeball it. If a near-charcoal color got snapped to your accent or vice versa, the palette has a gap or the source has AA noise. Tune <code>max_colors</code> and re-vectorize.</p><h3>Step 4 &#8212; SVGO multipass compression</h3><pre><code><code>svgo --multipass --precision=2 snapped.svg -o optimized.svg</code></code></pre><p>Expect 40&#8211;45% size reduction. SVGO merges multiple <code>&lt;path&gt;</code> elements into one path with <code>M</code> move-to jumps, so <code>grep -c '&lt;path'</code> will misleadingly report 1. The render is identical.</p><h3>Step 5 &#8212; Render to PNG and visually QC against the source</h3><p>Don&#8217;t skip this step. Every pipeline run can introduce a regression that only shows up by eye, and the earlier it gets caught the cheaper it is to fix.</p><pre><code><code>rsvg-convert optimized.svg -w 512 -o qc.png</code></code></pre><p>Open the PNG. Compare against the source. Look for:</p><ul><li><p>Vanishing details (a cream-on-cream feature that disappeared into the background)</p></li><li><p>Wrong-colored regions (a crimson zone rendered pink because palette-snap pulled it to the wrong neighbor)</p></li><li><p>Missing paths (something got SVGO&#8217;d away)</p></li><li><p>Bounding-box drift (the mark shifted in canvas)</p></li></ul><p>If anything&#8217;s wrong, fix the source step &#8212; re-vectorize with different params, edit the palette dict &#8212; don&#8217;t try to hand-patch the optimized SVG.</p><h3>Step 6 &#8212; Build the surface variants</h3><p>A production family needs:</p><ul><li><p><code>&lt;brand&gt;-mark-on-light.svg</code> &#8212; mark in dark on the brand&#8217;s light surface color, with a solid <code>&lt;rect&gt;</code> background baked in</p></li><li><p><code>&lt;brand&gt;-mark-on-dark.svg</code> &#8212; mark in light on the brand&#8217;s dark surface color, ditto</p></li><li><p><code>&lt;brand&gt;-mark-mono-black.svg</code> &#8212; single-color black, transparent background</p></li><li><p><code>&lt;brand&gt;-mark-mono-white.svg</code> &#8212; single-color white, transparent background</p></li><li><p>Optionally: <code>&lt;brand&gt;-mark-transparent.svg</code> &#8212; the cleanest version, no background</p></li></ul><p>The fast path: if your vectorized SVG uses <strong>class-based fills</strong> (<code>class="u-cream"</code>, <code>class="u-pink"</code>), every variant is a one-line <code>&lt;style&gt;</code> swap plus a <code>&lt;rect&gt;</code> background. Path data stays identical across all five variants. Each file ends up around 11 KB because the path data is shared semantically.</p><p>If the SVG uses inline <code>fill="#..."</code> attributes instead, either run a regex pass once to convert them to classes, or make N copies and find-replace per variant. The first approach is much less work as soon as you need more than two variants.</p><p><strong>Bake a </strong><code>&lt;rect width="100%" height="100%" fill="#..."/&gt;</code><strong> background into the on-light and on-dark files.</strong> It&#8217;s tempting to leave the surface color to the consumer&#8217;s CSS, but downstream consumers will eventually forget to set it, and the mark will vanish on the wrong surface when they do.</p><h3>Step 7 &#8212; Build the favicon</h3><p>The favicon is the one place in a brand system where <code>prefers-color-scheme</code> inside the SVG is the right abstraction. Browser chrome IS the surface. It changes when the user changes their OS theme. The favicon needs to follow it.</p><pre><code><code>&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"&gt;
&lt;style&gt;
  .u-cream { fill: #F5E9D3; }
  .u-pink  { fill: #FF2D87; }
  .u-bg    { fill: #1A0E0F; }
  @media (prefers-color-scheme: dark) {
    .u-cream { fill: #1A0E0F; }
    .u-bg    { fill: #F5E9D3; }
  }
&lt;/style&gt;
&lt;rect class="u-bg" width="1024" height="1024"/&gt;
&lt;!-- the rest of the mark paths --&gt;
&lt;/svg&gt;</code></code></pre><p>Then generate the raster fallbacks at the standard sizes:</p><pre><code><code>for size in 16 32 48 180; do
  rsvg-convert mark-favicon.svg -w $size -h $size -o favicon-${size}.png
done</code></code></pre><p>Open the 16/32 PNGs and look at them.</p><p><strong>Primary marks with internal detail &#8212; aviators, monocles, decorative hardware, grip wraps &#8212; rarely survive at 16&#8211;32 px.</strong> They tend to render as muddy pixel blobs at those sizes. There are three reasonable ways to handle that:</p><ul><li><p>Substitute a simpler mark at small sizes. If the brand has a sigil or monogram variant, use it for the 16/32 PNG fallbacks. Different SVG sources, same family. Document the substitution.</p></li><li><p>Hand-redraw a 16-px variant. A dedicated favicon mark with the silhouette plus one feature.</p></li><li><p>Accept a recognizable color blob. If the brand&#8217;s color signature is distinctive enough, even a muddy 16-px favicon reads as &#8220;that brand&#8221; by hue alone. Document it as a deliberate tradeoff.</p></li></ul><h3>Step 8 &#8212; Write the README</h3><p>Save a <code>README.md</code> next to the production SVG family that documents:</p><ul><li><p>The variant matrix &#8212; file &#8594; which surface &#8594; which use case</p></li><li><p>The locked palette hex values</p></li><li><p>Provenance &#8212; source PNG, generation params, scripts run</p></li><li><p>What&#8217;s deferred and why (wordmarks composited from real type instead of traced, designer cleanup pass pending)</p></li></ul><p>A future agent or designer landing on the directory cold will save half a day reading this rather than reverse-engineering it.</p><h2>Gotchas worth pre-reading</h2><p>These are the gotchas that have actually cost time in practice. Most of them only bite once, but each one tends to cost some real time the first time you run into it.</p><p><strong>The subscription mix-up.</strong> Vectorizer.AI sells web-app and API plans that look almost identical, cost the same, and sit side-by-side on the pricing page. The only reliable way to tell which one your account ended up on is to hit the account endpoint and read the plan name back. Skipping that check usually shows up as every API call returning 402, until it eventually clicks that the wrong subscription is the one that got bought.</p><p><strong>Transparent PNG fragmentation.</strong> Feeding a chroma-keyed transparent PNG into the tracer tends to produce something like 5,000 paths and a 900 KB SVG that isn&#8217;t really usable in production. The neural tracer treats every band of anti-aliased halo as a distinct color and emits a separate path for each one. About three credits is the going rate for learning that lesson, so the safer move is to always flatten against a solid color before tracing.</p><p><code>max_colors</code><strong> quantization.</strong> If you set <code>max_colors</code> equal to your palette size, the API will quantize the least-frequent color out of the output entirely. A crimson zone can come back as a hot pink, and the snap script in Step 3 can&#8217;t recover what was never traced in the first place. The safer default is to set <code>max_colors</code> to palette size plus one and let palette-snap clean up the drift afterward.</p><p><strong>Wordmarks traced as polygons.</strong> If the source PNG has text rendered into it &#8212; a &#8220;PROPER NAME LTD&#8221; lockup beneath the mark, for instance &#8212; Vectorizer.AI will trace each letter as a polygon. Traced letterforms muddy at small sizes in ways that look unprofessional even before anyone can articulate why. The fix is to strip the wordmark paths from the SVG and re-add the wordmark as a real <code>&lt;text&gt;</code> element with the actual font, composited into a lockup at the use site.</p><p>The fast way to strip them: each wordmark letter has a <code>transform="translate(X,Y)"</code> where Y falls inside a narrow band &#8212; say 800&#8211;920 for letters along the bottom of a 1024-pixel canvas. A regex over that Y range removes them in one pass.</p><pre><code><code>pattern = re.compile(
    r'&lt;path\b[^&gt;]*?\btransform="translate\(\s*[0-9.]+\s*,\s*(?:8[0-9][0-9]|9[01][0-9])\s*\)"[^&gt;]*?/&gt;',
    re.DOTALL,
)
cleaned = pattern.sub('', svg_content)</code></code></pre><p>Adjust the Y range for your canvas size and wordmark position.</p><p><strong>Primary mark doesn&#8217;t survive 16&#8211;32 px.</strong> Covered in Step 7. Use a simpler sigil at small sizes, and document the substitution in the README so the next person knows the favicon and the primary mark aren&#8217;t drawn from the same SVG source.</p><h2>What the pipeline gives up</h2><p>Vectorizer.AI&#8217;s output is about 30&#215; cleaner than vtracer&#8217;s on the same source &#8212; one unified silhouette vs. 31 fragments &#8212; but it is <strong>still not anchor-minimized.</strong> A typical Vectorizer.AI mark has 130&#8211;150 anchors where a hand-cleaned version would have 40&#8211;60. At digital sizes and most physical sizes this is invisible. At billboard scale, storefront signage, or embroidery runs above ~100 units, the lumpy bezier curvature shows.</p><p>The pipeline as documented produces digital-grade and most-physical-grade output. If the brand commits to a 30-foot sign or an embroidery run of meaningful volume, add a designer cleanup pass on the primary mark in Illustrator. Budget 30&#8211;60 minutes per mark, around $100&#8211;150 for a freelance designer.</p><h2>Worked example</h2><p>Imagine a fictional brand <strong>Meridian</strong>, a wayfinding studio. Mark: a stylized compass rose. Palette: 4 colors.</p><pre><code><code>PALETTE = {
    "#1A2540": "navy",
    "#D9A857": "gold",
    "#F0E6D2": "paper",
    "#0A0A0F": "ink",
}</code></code></pre><p>The run:</p><pre><code><code># 1. Verify source
file meridian-compass.png
# PNG image data, 1024 x 1024, 8-bit/color RGB &#8212; good

# 2. Vectorize
curl -sS https://vectorizer.ai/api/v1/vectorize \
  -u "$VECTORIZER_AI_APP_ID:$VECTORIZER_AI_API_SECRET" \
  -F image=@meridian-compass.png \
  -F mode=production \
  -F output.file_format=svg \
  -F processing.max_colors=5 \
  -F output.svg.version=svg_1_1 \
  -F output.svg.fixed_size=true \
  -o meridian-raw.svg

# 3. Palette snap (edit PALETTE dict in the script first)
python3 palette-snap.py meridian-raw.svg meridian-snapped.svg

# 4. SVGO
svgo --multipass --precision=2 meridian-snapped.svg -o meridian.svg

# 5. QC
rsvg-convert meridian.svg -w 512 -o /tmp/qc.png
# Open qc.png, compare to source. Looks correct? Continue.

# 6. Build surface variants &#8212; duplicate file, swap &lt;style&gt;
#    Each variant becomes meridian-on-light.svg, meridian-on-dark.svg, etc.

# 7. Favicon
rsvg-convert meridian-favicon.svg -w 32 -h 32 -o meridian-favicon-32.png
# 32-px favicon reads as a compass rose, not a blob: ship it.
# Otherwise substitute a simpler sigil at 16/32.

# 8. README &#8212; document the variant matrix, palette, provenance.</code></code></pre><p>For the cost of around an hour and 4&#8211;8 API credits ($1&#8211;2 USD), you end up with a primary mark, a sigil, a horizontal lockup, four surface variants, and the full favicon set.</p><h2>Recommended directory layout</h2><pre><code><code>&lt;brand&gt;/svg/
&#9500;&#9472;&#9472; production/
&#9474;   &#9500;&#9472;&#9472; primary/
&#9474;   &#9474;   &#9500;&#9472;&#9472; &lt;brand&gt;-primary-on-light.svg
&#9474;   &#9474;   &#9500;&#9472;&#9472; &lt;brand&gt;-primary-on-dark.svg
&#9474;   &#9474;   &#9500;&#9472;&#9472; &lt;brand&gt;-primary-mono-black.svg
&#9474;   &#9474;   &#9500;&#9472;&#9472; &lt;brand&gt;-primary-mono-white.svg
&#9474;   &#9474;   &#9492;&#9472;&#9472; &lt;brand&gt;-primary-transparent.svg
&#9474;   &#9500;&#9472;&#9472; sigil/
&#9474;   &#9474;   &#9500;&#9472;&#9472; &lt;brand&gt;-sigil-on-light.svg
&#9474;   &#9474;   &#9492;&#9472;&#9472; &lt;brand&gt;-sigil-on-dark.svg
&#9474;   &#9500;&#9472;&#9472; horizontal/
&#9474;   &#9474;   &#9492;&#9472;&#9472; &lt;brand&gt;-horizontal-on-dark.svg
&#9474;   &#9500;&#9472;&#9472; favicon/
&#9474;   &#9474;   &#9500;&#9472;&#9472; &lt;brand&gt;-favicon.svg
&#9474;   &#9474;   &#9500;&#9472;&#9472; &lt;brand&gt;-favicon-16.png
&#9474;   &#9474;   &#9500;&#9472;&#9472; &lt;brand&gt;-favicon-32.png
&#9474;   &#9474;   &#9500;&#9472;&#9472; &lt;brand&gt;-favicon-48.png
&#9474;   &#9474;   &#9492;&#9472;&#9472; &lt;brand&gt;-favicon-180.png
&#9474;   &#9492;&#9472;&#9472; README.md
&#9492;&#9472;&#9472; vectorizer-ai/
    &#9500;&#9472;&#9472; raw/        # untouched API output
    &#9500;&#9472;&#9472; snapped/    # palette-snapped, pre-SVGO
    &#9500;&#9472;&#9472; optimized/  # SVGO'd, ready to copy to production/
    &#9492;&#9472;&#9472; _failed-*/  # quarantined failed runs</code></code></pre><p>Keep the working files in <code>vectorizer-ai/</code> &#8212; useful for forensics and re-runs. Don&#8217;t import from there at use sites. Always import from <code>production/</code>.</p><h2>Closing</h2><p>The thing that makes this pipeline work isn&#8217;t any single step on its own. It&#8217;s the order, combined with knowing which mistakes cost real money along the way. Most of that cost gets paid by the first person to walk through each gotcha, and the point of writing it down is that the second person shouldn&#8217;t have to.</p><p>For the cost of about an hour of focused work and a couple of dollars in API spend, the pipeline produces a working brand-mark family that includes a primary mark, a sigil, a horizontal lockup, four surface variants, and a full favicon set. Worth knowing about, if you&#8217;re going to be doing this kind of work more than once.</p>]]></content:encoded></item><item><title><![CDATA[We are moving towards software-on-demand]]></title><description><![CDATA[We have stepped into a world, where information tools can, will, and should be built on-demand for the circumstances and situations at hand.]]></description><link>https://www.smokesomepaint.com/p/we-are-moving-towards-software-on</link><guid isPermaLink="false">https://www.smokesomepaint.com/p/we-are-moving-towards-software-on</guid><dc:creator><![CDATA[Cully Wakelin]]></dc:creator><pubDate>Fri, 08 May 2026 14:45:01 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!cBmX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cBmX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cBmX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!cBmX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!cBmX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!cBmX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cBmX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cBmX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!cBmX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!cBmX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!cBmX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F017408de-45c2-4c81-a44c-37bc6ae71968_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There were a few years where I followed the <a href="https://github.com/facebook/docusaurus">Docusaurus project</a> closely. I relied on it to make shipping software documentation relatively easy.<br><br>Another tool I adored was <a href="https://www.kapa.ai/">Kapa.ai</a> - as close to a &#8220;drop-in-and-go&#8221; AI developer assistant as you could get.<br><br>I relied on them because they were tried and tested purpose-built tools with decent communities supporting them that offered 90-95% of the functionality I needed without me having to build anything myself.</p><p>And to be clear, there is no way 1 person could build and maintain custom built systems like that.</p><p>At least&#8230; that&#8217;s how it used to be&#8230;</p><p>I would say the real deep shifts in how we work started around January and February of 2026.</p><p>And then last month, April 2026, it became obvious that the scales had completely shifted.<br><br>The golden rule that a company should only R&amp;D its own product and source whats already built for auxiliary systems, at least when it comes to software, has crumbled.<br><br>I no longer have to settle for compromises when stringing already-built-tools together.<br><br>We have stepped into a world, where information tools can, will, and should be built on-demand for the circumstances and situations at hand.<br><br>Last month, it took just 2 days to rip out both Docusaurus and Kapa.ai and replace it with a completely custom built system that makes 0 compromises on desired functionality and level of automation.</p><p>A validation harness provides assurances that our information assets are in better shape than ever.<br><br>At the moment, you still need someone with software engineering experience to steer the iterations is takes to produce something scalable, reliable, and robust. But that gap is diminishing quickly.</p><p></p>]]></content:encoded></item><item><title><![CDATA[I built an AI newspaper that runs itself every morning]]></title><description><![CDATA[I did it to learn about the community my kids play sports in. And It's working!]]></description><link>https://www.smokesomepaint.com/p/i-built-an-ai-newspaper-that-runs</link><guid isPermaLink="false">https://www.smokesomepaint.com/p/i-built-an-ai-newspaper-that-runs</guid><dc:creator><![CDATA[Cully Wakelin]]></dc:creator><pubDate>Mon, 13 Apr 2026 18:25:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!1pAp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1pAp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1pAp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!1pAp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!1pAp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!1pAp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1pAp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1pAp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!1pAp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!1pAp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!1pAp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1f3c509-171c-4be1-b081-a80f2df1f97a_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>At 6am, the harvest agent wakes up and crawls every source I&#8217;ve pointed it at &#8212; local news sites, organization pages, RSS feeds. It pulls everything football-related into a staging table and tracks which sources keep delivering and which ones have gone dry.</p><p>At 7am, the curator agent reads everything harvested in the last 26 hours, checks it against every story ever published &#8212; all-time deduplication &#8212; and asks Claude Sonnet to pick the single best story. The editorial agent constraint narrows it down to one.</p><p>At 8am, the broadcast agent reads today&#8217;s published digest and sends it out via email.<br><br>Once a week, a separate discovery agent goes looking for new sources. It searches the web, checks RSS feeds on organization sites, and uses a small language model to score whether each candidate is actually relevant. Good ones get added to the crawl list automatically.</p><p>I don't touch it. I don't even know what the story is until I read my own email.</p><p>It&#8217;s <a href="https://www.pitchroots.ca/digest">Pitch Digest</a> &#8212; the editorial layer of PitchRoots, a community football information platform I&#8217;ve been tinkering with. One story per day. No feed. No noise.</p><p>I built it because I don't know anything. My son plays football in Spruce Grove and I always feel like I am disoriented with the broader Edmonton football ecosystem &#8212; how Tiers work, which clubs cater to which players, and when tournaments and training programs kick off. I built Pitch Digest to keep me in the loop about football related people, places, and events.</p><p>Pitch Digest is a four-stage pipeline. Each stage is its own agent, running on its own schedule. Four agents. Four cron schedules. Zero human intervention on a normal day.</p><p>What makes this reliable enough to leave it unattended?</p><p>If you've ever built something like this &#8212; a multi-step workflow where each step calls an API, writes to a database, or invokes an LLM &#8212; you know the failure mode. Step three fails. You retry the whole thing. Step one re-crawls the web. Step two re-processes everything. Your LLM bill doubles. Or worse: step three partially completes, you retry, and now you've got duplicate data.<br><br>The AI part works. The plumbing breaks constantly. Network timeouts. Rate limits. Cold starts. Partial writes. Every step is a new opportunity for the whole thing to fall apart.</p><p>And this is a daily pipeline. If it breaks on Tuesday and I don't notice until Thursday, that's two days of silence.</p><p>I outsourced all of that stress to <a href="https://www.resonatehq.io/">Resonate</a>.</p><p>Resonate is a durable execution engine. It tracks the progress of each step in a workflow and guarantees that if something fails, it picks up exactly where it left off. From the failed step, not the beginning.</p><p>Each agent is a generator function. Every step yields a durable checkpoint.</p><p>The curator agent, for example, has five steps: check if a digest was already published today, load the recent harvest, load the dedup list, ask Claude to pick a story, and publish it. Each of those steps is individually durable. If the LLM call in step four times out, Resonate retries step four. Steps one through three don't re-execute &#8212; their results are already stored.</p><p>On resume, completed steps replay instantly from server state. No re-execution, no duplicate writes. The generator pattern makes this natural &#8212; you write your workflow as a straight-line sequence of steps, and the durability layer handles the rest.</p><p>It spins up. It processes work. It spins down.</p><p>These agents run on Supabase Edge Functions &#8212; serverless, pay-per-invocation. Resonate's HTTP-push mode sends execution callbacks to each function endpoint. The agents don't poll. They don't hold connections open. They wake up, do their work, checkpoint progress, and go back to sleep.</p><p>The whole thing costs almost nothing to run. Four functions, four cron triggers, a few LLM calls per day. The infrastructure matches the workload: bursty, short-lived, cheap at rest.</p><p>Everything can be serverless because Resonate handles the state. All the durability, progress tracking, and retry logic lives on the Resonate server. The functions just do work and yield results.</p><p>And Resonate offers a great observability.</p><p>Every promise in Resonate has a state &#8212; pending, resolved, rejected &#8212; along with its input, output, timestamps, and its position in the call hierarchy. That's not logging I added. That's just what durable promises are. The execution history is the observability layer.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vmwz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vmwz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png 424w, https://substackcdn.com/image/fetch/$s_!vmwz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png 848w, https://substackcdn.com/image/fetch/$s_!vmwz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png 1272w, https://substackcdn.com/image/fetch/$s_!vmwz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vmwz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png" width="1250" height="1089" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1089,&quot;width&quot;:1250,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:82309,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.smokesomepaint.com/i/194098632?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vmwz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png 424w, https://substackcdn.com/image/fetch/$s_!vmwz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png 848w, https://substackcdn.com/image/fetch/$s_!vmwz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png 1272w, https://substackcdn.com/image/fetch/$s_!vmwz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7bc398e-f05e-4873-8a57-53d08f0a7413_1250x1089.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is the call graph for a single morning's pipeline run. Source Harvest fans out into individual scout promises &#8212; one per source URL. Each scout shows whether it found new content or came back empty. Green means it delivered. Red means the source was down or had nothing new. The pipeline flows left to right: harvest &#8594; curator &#8594; corpus librarian &#8594; broadcast. Every node shows its duration.<br><br>I didn't instrument this. I didn't add tracing spans or wire up OpenTelemetry. The promise tree <em><strong>is</strong></em> the trace. Resonate tracks every function invocation as a promise in a parent-child hierarchy, so the call graph builds itself.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2Jnq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2Jnq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png 424w, https://substackcdn.com/image/fetch/$s_!2Jnq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png 848w, https://substackcdn.com/image/fetch/$s_!2Jnq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png 1272w, https://substackcdn.com/image/fetch/$s_!2Jnq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2Jnq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png" width="1420" height="1205" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1205,&quot;width&quot;:1420,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:205521,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.smokesomepaint.com/i/194098632?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2Jnq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png 424w, https://substackcdn.com/image/fetch/$s_!2Jnq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png 848w, https://substackcdn.com/image/fetch/$s_!2Jnq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png 1272w, https://substackcdn.com/image/fetch/$s_!2Jnq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2db03abe-8cb8-4622-abd6-1a06587820db_1420x1205.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The dashboard pulls from the same promise data. Today's published story at the top. Pipeline status showing whether each agent succeeded, failed, or hasn't run yet. Source health tracking which of the 25 sources are producing and which have gone dry &#8212; so I can see at a glance if a source needs to be dropped or investigated.<br><br>With most pipeline architectures, you'd bolt on a separate observability stack &#8212; custom dashboards, log aggregation, the works. Correlation IDs, trace contexts, status pages. A whole second project.<br><br>With durable promises, the execution model itself is queryable. Every promise has a known state. Every parent-child relationship is tracked. You just read the data that's already there.<br><br>The hard part of an AI editorial pipeline isn't the AI. Claude is genuinely good at picking stories and writing summaries. The hard part is everything around it &#8212; making sure the harvest actually ran, making sure a failed broadcast doesn't silently eat the whole day's output.<br><br>Durable execution turns those problems from "things I have to monitor and manually fix" into "things the system handles."</p><p>The pipeline has been running for weeks now. I check in occasionally. It just works.</p><p>That's the bar for anything I want to run unattended. Not "it works when everything goes right." It works when things go wrong, and I don't have to know about it.</p><p>And the best part? Here are four real headlines my own tool brought me in its first week:</p><ul><li><p><strong><a href="https://albertasoccer.com/news/canada-soccer-national-championships-2027-2029-request-for-proposal/">Alberta Communities Invited to Bid on Hosting Canada Soccer National Championships Through 2029</a></strong></p></li><li><p><strong><a href="https://www.pitchroots.ca/digest/2026-04-12">Spruce Grove Soccer Hosting Free Grassroots Coaching Courses This Month &#8212; Spots Still Available</a></strong></p></li><li><p><strong><a href="https://www.pitchroots.ca/digest/2026-04-11">Alberta Earns Extra U17 Girls Spot at Canada Soccer Youth National Championship</a></strong></p></li><li><p><strong><a href="https://www.pitchroots.ca/digest/2026-04-10">Women&#8217;s Football &#9917; Takes the Big Screen: &#8216;The Pitch&#8217; Screens in Edmonton This Tuesday</a></strong></p></li></ul><p>I built this to learn about the community my kids play sports in.  And It's working!</p>]]></content:encoded></item><item><title><![CDATA[A gap in Alberta's open data]]></title><description><![CDATA[Production data on every well in the province is free and public. Ownership data on the same wells is structurally not.]]></description><link>https://www.smokesomepaint.com/p/a-gap-in-albertas-open-data</link><guid isPermaLink="false">https://www.smokesomepaint.com/p/a-gap-in-albertas-open-data</guid><dc:creator><![CDATA[Cully Wakelin]]></dc:creator><pubDate>Sun, 12 Apr 2026 16:02:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!SVEe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SVEe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SVEe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!SVEe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!SVEe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!SVEe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SVEe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SVEe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!SVEe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!SVEe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!SVEe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F613796bc-8fd3-4531-a935-831ef48ee1a5_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Alberta publishes an extraordinary amount of data about its oil and gas industry.</p><p>Petrinex makes the volumetrics for every well in the province available as a public download &#8212; operator, formation, volumes, injection, disposition &#8212; monthly, going back to 1962, free, no login. It&#8217;s one of the more open production datasets of any major oil jurisdiction. Texas publishes a lot too, but Alberta&#8217;s is cleaner at the well level.</p><p>About 8% of Alberta&#8217;s mineral rights are freehold &#8212; privately owned rather than Crown. A further ~10% is federal (national parks, First Nations reserves). The remaining ~81% belongs to the province. </p><p>On freehold land, royalties from production flow to whoever owns the minerals underneath rather than to the Crown. Most of that ownership is corporate &#8212; railways (CN is one of the largest freehold mineral owners in Alberta, a holdover from a 19th-century land grant), royalty trusts like Prairie Sky and Freehold Royalties, religious organizations, family trusts. Roughly 10% of freehold mineral rights are held by individuals.</p><p>The production side of this &#8212; what comes out of the ground &#8212; is, as above, fully public. The ownership side is not. There is no public registry of freehold mineral rights owners or freehold royalty recipients in Alberta. You can pay Land Titles to pull a certificate of title for a specific legal description and see one owner, but there is no name search, no bulk dataset, no aggregated list. Commercial vendors like geoLOGIC and Enverus compile this from title abstracts for industry clients, but that&#8217;s a paid private product, not a public one.</p><p>The province itself has the data. It has to. Alberta levies a freehold mineral tax under the Freehold Mineral Rights Tax Act, assessed annually, billed by name to every owner whose share is worth more than $100. The assessments come off the mineral certificate of title. So somewhere in the Alberta Energy ministry there&#8217;s a complete list of freehold royalty recipients with names, addresses, and taxable values. That list isn&#8217;t published, and publishing it would require legislative authorization because the contents are personal information under FOIPP &#8212; the same kind of legislative act BC and Quebec passed when they built their beneficial ownership registries. No comparable bill has been tabled in Alberta.</p><p>The Non-Crown Mineral Ownership dataset, which Alberta does publish on open.alberta.ca, is a spatial file showing where in the province the mineral rights are non-Crown &#8212; polygons covering every patch of freehold and federal land. The geometry is there. The owner names are not. So the public version of the data answers &#8220;where are these rights&#8221; but not &#8220;whose are they.&#8221;</p><p>So, that&#8217;s the observation. Production data is exceptionally open. Ownership data, on the same wells, is structurally not. Both halves are deliberate &#8212; one is published as a matter of regulatory routine, the other is held back as a matter of privacy law. They&#8217;re not in tension exactly. They&#8217;re just an unusual pair of choices to find sitting next to each other.</p><p>I&#8217;m wary of reading too much into this as it might be less unusual than I think &#8212; Texas and Oklahoma freehold ownership is similarly opaque, and FOIPP-style protections on tax rolls are normal across Canada. It might also be more unusual than I think, in the sense that the production transparency really is unusually deep and the contrast really is unusually sharp.</p><p>If you work in Alberta land, freehold royalties, or mineral title abstracting and I&#8217;ve gotten any of this wrong &#8212; the percentages, the mechanism of the freehold mineral tax, the availability of any registry I missed &#8212; I&#8217;d like to know. The post is an observation, not a conclusion, and I&#8217;d rather correct it than defend it.</p>]]></content:encoded></item><item><title><![CDATA[Is suspension a primitive or a trick?]]></title><description><![CDATA[Resonate is addressing the problem of suspension by pinning the answer to a protocol instead of a runtime.]]></description><link>https://www.smokesomepaint.com/p/is-suspension-a-primitive-or-a-trick</link><guid isPermaLink="false">https://www.smokesomepaint.com/p/is-suspension-a-primitive-or-a-trick</guid><dc:creator><![CDATA[Cully Wakelin]]></dc:creator><pubDate>Wed, 08 Apr 2026 16:41:33 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!GSif!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GSif!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GSif!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!GSif!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!GSif!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!GSif!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GSif!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GSif!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!GSif!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!GSif!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!GSif!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F565efa7f-7dd3-4588-94f3-6791bb310c80_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Have you read this? <a href="https://www.inngest.com/blog/hanging-promises-for-control-flow">Hanging Promises for Control Flow.</a></p><p>So, if I understand it correctly, you&#8217;re inside a serverless function. You hit a step you can&#8217;t finish in this invocation. You need to get the user&#8217;s <code>async</code> function off the stack without throwing &#8212; because user <code>try/catch</code> might swallow the signal. So you <code>await</code> a promise that never resolves. Function suspends, process gets torn down, GC cleans up the frame, fresh invocation replays from the top using a memoization map of finished steps.</p><p>Clever, though isn&#8217;t this a workaround for a problem that only exists because suspension isn&#8217;t a first-class thing the runtime owns?</p><p>Seems like the whole maneuver leans on a property of the language&#8217;s runtime, where each language would require its own version of the trick with its own caveats.</p><p>I currently work at <span class="mention-wrap" data-attrs="{&quot;name&quot;:&quot;Resonate HQ&quot;,&quot;id&quot;:3947434,&quot;type&quot;:&quot;pub&quot;,&quot;url&quot;:&quot;https://open.substack.com/pub/resonatehqio&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/26ad1caa-901d-4c62-8d7c-03bc0ee34fc1_645x645.png&quot;,&quot;uuid&quot;:&quot;30f87950-3226-4430-a739-3457bb343cb9&quot;}" data-component-name="MentionToDOM"></span> where we are betting that suspension shouldn&#8217;t be a clever thing the runtime sneaks past user code &#8212; it should be a <em>protocol</em>. </p><p>Coincidentally enough, the protocol calls for a <a href="https://docs.resonatehq.io/">durable promise</a> that lives outside any single process, or any single language.</p><p>When a function awaits a durable promise, the runtime parks the computation. Promise resolves &#8212; same process, different process, one that doesn&#8217;t exist yet &#8212; computation comes back. No exception to dodge, no GC trick to lean on. The <code>await</code> (or in the current Resonate Typescript SDK version its <code>yield</code>) means what it says.</p><p>If that&#8217;s right, the hanging-promise technique is a great answer <em>inside one language&#8217;s constraints</em>, but Resonate is making the same problem moot one layer down by pinning the answer to a protocol instead of a runtime.</p><p>If you think I&#8217;ve got something backwards, tell me.</p>]]></content:encoded></item><item><title><![CDATA[Vibe coding with 15 years of context]]></title><description><![CDATA[The workspace is the brain. Claude is the hands.]]></description><link>https://www.smokesomepaint.com/p/vibe-coding-with-15-years-of-context</link><guid isPermaLink="false">https://www.smokesomepaint.com/p/vibe-coding-with-15-years-of-context</guid><dc:creator><![CDATA[Cully Wakelin]]></dc:creator><pubDate>Tue, 07 Apr 2026 15:31:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!4DIC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4DIC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4DIC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!4DIC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!4DIC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!4DIC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4DIC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png" width="1024" height="608" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:608,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4DIC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png 424w, https://substackcdn.com/image/fetch/$s_!4DIC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png 848w, https://substackcdn.com/image/fetch/$s_!4DIC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png 1272w, https://substackcdn.com/image/fetch/$s_!4DIC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf50d9b1-8083-4942-abba-fb87300302b9_1024x608.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The vibe in "vibe coding" is the developer's intuition about what the code should do, how it should fit together, and when something's off.</p><p>Not the agent, or the model. Not your keyboard. The vibe is <em><strong>you</strong></em>.</p><p>I've been shipping software for 15 years &#8212; tech support, devrel, docs, product, engineering, startups that made it, startups that didn't. When I sit down with Claude Code now, I'm not just prompting an AI to write or build something for me. I'm compressing 15 years of pattern recognition into a structured environment where an AI can actually be useful.</p><p>That distinction matters. And I think most of the "vibe coding" discourse misses it completely, though often humorously.</p><p>The real magic happens before Claude writes any executable code.</p><p>By the time I say, &#8220;hey Claude, build me a dashboard&#8221;, I've already put together rules to follow, skill directories, and decision structures that Claude reads at the start of every session. Every project has an AGENT.md &#8212; a structured document that tells any AI agent exactly what the project is, what the stack looks like, what the architecture rules are, and what not to break. Above that, a workspace-level orientation file maps every project, defines data boundaries, and enforces privacy rules. Below that, a knowledge layer &#8212; domain-specific documents covering everything from data source registries to privacy regulations to tax law to investment frameworks, etc.</p><p>The AI doesn't guess. It reads. Every new Claude session starts fresh &#8212; no memory of yesterday &#8212; but within 30 seconds it has full context and is capable of producing quality output.</p><p>In other words, my 15 years of experience is now applied to the structure that makes Claude&#8217;s output predictable in quality.</p><p>And it's the same idea all the way down.</p><p>This pattern &#8212; write a structured spec, then generate the output &#8212; isn't just how I work with Claude Code. It's the same principle behind what we're building at <span class="mention-wrap" data-attrs="{&quot;name&quot;:&quot;Resonate HQ&quot;,&quot;id&quot;:3947434,&quot;type&quot;:&quot;pub&quot;,&quot;url&quot;:&quot;https://open.substack.com/pub/resonatehqio&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/26ad1caa-901d-4c62-8d7c-03bc0ee34fc1_645x645.png&quot;,&quot;uuid&quot;:&quot;8f5c3c7c-26bc-4440-b40d-3fb2f2f2c213&quot;}" data-component-name="MentionToDOM"></span>.</p><p>Resonate is a durable execution platform. Instead of hand-writing server components for every possible deployment topology, we wrote a formal protocol specification. Language-agnostic. Transport-agnostic. Storage-agnostic. Then we generate the components from the spec.</p><p>Need a server targeting Postgres + HTTP? Generate it. Need one for Cloudflare Workers + D1? Generate that. Every line of code exists because your stack needs it. Nothing else ships.</p><p>The parallel is almost too clean: I don't hand-write code for each project from scratch anymore. I write structured context &#8212; rules, skills, architecture docs &#8212; and let Claude generate the implementation. Same mentality. Specify the constraints, generate the output, validate against the spec.</p><p>In both cases, the quality of the output is determined by the quality of the specification. Garbage spec, garbage code. Precise spec, precise code. The generation step is the easy part.</p><p>Fresh instances and structured workspaces are simple but key aspects to quality Claude usage.</p><p>One thing people don't realize about working with Claude Code: every session is a blank slate. The model doesn't remember what you did yesterday. It doesn't learn your preferences over time. Every conversation starts from zero.<br><br>That sounds like a limitation. It's actually a usable feature.<br><br>Because every session is fresh, you're forced to make your context explicit. You can't rely on "Claude knows what I mean." You have to write it down. The rules, the architecture, the boundaries, the patterns &#8212; all of it lives in files, not in your head.<br><br>And once it's in files, any agent can read it. Not just Claude. A CI bot. A code review tool. A new team member. The structured workspace becomes a unique product of its own.<br><br>I spin up fresh Claude instances for specific tasks all the time. One for frontend work, reading the component library docs. One for data pipeline work, reading the data source registry. One for financial modeling, reading the tax reference corpus. Each one gets exactly the context it needs and nothing it doesn't.<br><br>The workspace is the brain. Claude is the hands.<br><br>If you're new to programming and you're vibe coding, you're probably generating code you can't fully evaluate. You'll probably accept a function that works but scales quadratically because you've never been burned by that. You'll probably let the AI set up auth in a way that's fine for a tutorial and catastrophic in production.<br><br>That's not a reason to stop. It's a reason to build your own pattern library as you go.<br><br>Every time Claude generates something you don't understand, that's a learning opportunity you're either taking or skipping. The developers who use AI to learn faster will end up with great instincts &#8212; just compressed into fewer years. The ones who use AI to avoid learning will plateau at "can generate code, can't evaluate code."</p><p>Velocity comes from having pre-built component libraries, a clear architecture, and an AI that reads the entire project context before writing a line.</p><p>One danger for experienced devs isn't going too slow. It's going so fast that everything feels worth building. The cost to produce software has collapsed for everybody. Which means speed isn't the differentiator anymore. Judgment is. Knowing which direction to go is what matters.<br><br>The vibe has always been you &#8212; about knowing what to make, how it should fit together, and when something's off. AI has just become fast enough to match whatever level of that you bring.</p>]]></content:encoded></item><item><title><![CDATA[The Wisdom Paradox]]></title><description><![CDATA[To be human is to wrestle with this paradox, again and again.]]></description><link>https://www.smokesomepaint.com/p/the-wisdom-paradox</link><guid isPermaLink="false">https://www.smokesomepaint.com/p/the-wisdom-paradox</guid><dc:creator><![CDATA[Cully Wakelin]]></dc:creator><pubDate>Thu, 08 May 2025 17:51:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa12244d0-7271-4573-8a28-917e74ac5650_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There's a paradox I encounter every day.</p><p>I see it in engineering discussions, cultural debates, even in personal growth.</p><p>Here's the paradox:</p><p>Intertwingularity is real. Knowledge doesn't sit in neat categories. There are no absolute definitions or rigid boundaries. Everything flows, shifts, and overlaps. The world is messy, fluid, and in constant motion.</p><p>Yet, as human beings, we depend on models and definitions. Our ability to communicate, teach, and learn hinges on structure&#8212;on drawing lines, labeling things, and following rules. Without these models, society can't function.</p><p>This contradiction echoes the Dreyfus Model of Skill Acquisition.</p><p>According to the model, beginners need clear rules to operate. They follow step-by-step instructions because they lack the experience to interpret complexity. But as they gain mastery, their intuition grows. Experts don't follow rigid instructions&#8212;they feel what to do. Their actions are shaped by context, not checklists.</p><p>I&#8217;ve seen this in software engineering. Concepts like &#8220;durable execution&#8221; &#8220;event-driven architecture,&#8221; or &#8220;serverless&#8221; are abstract and often fluid in practice. But to discuss them, we need shared mental models. We define things like &#8220;pure functions&#8221; or &#8220;side effects&#8221; not because they exist in perfect clarity, but because without these definitions, communication becomes impossible. A beginner might take these models literally. An expert knows they&#8217;re just tools&#8212;useful simplifications to help navigate complexity.</p><p>This paradox explains many individual and societal disagreements.</p><p>Take politics. Experts often speak in nuances. But the public&#8212;understandably&#8212;wants clarity. Politicians exploit this tension. They weaponize simplicity, offering rigid models that feel safe, even if they mislead. They use the paradox to win arguments, votes, or influence&#8212;often at the expense of truth.</p><p>And yet, we need those simple models.</p><p>Most of us are beginners in many areas of life. That's not a flaw&#8212;it's just reality. Society needs frameworks and definitions to function. Teachers can't educate without them. Businesses can't operate without clear processes. Even something as basic as a map requires us to define what a &#8220;road&#8221; is and where it begins and ends.</p><p>Without definitions, mental models collapse. And when they do, communication breaks down. Education fails. Society stumbles.</p><p>But those same models create tension. Experts may find them limiting. Beginners may find them confusing. And both may talk past each other without realizing why.</p><p>As long as we're human, we'll live with this paradox.</p><p>We need structure to grow, and we need to outgrow that structure to master.<br></p><p></p>]]></content:encoded></item></channel></rss>