← back to Goodquestion Ai
Backfill Apr11-Jul28 journal gap: 16 generalized milestone posts + v2 content plan
be3a3f959231263b03ce536068bdf53ede208204 · 2026-07-28 09:43:36 -0700 · Steve Abrams
All posts mapped to real shipped work, generalized (no vendor/client names,
no pricing/infra secrets) per the new content plan's no-trade-secrets rule.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
A docs/plans/2026-07-28-content-plan.mdA src/content/posts/2026-04-18-one-template-a-family-of-niche-storefronts.mdA src/content/posts/2026-04-30-retiring-a-bash-recipe-for-a-real-command.mdA src/content/posts/2026-05-12-a-self-serve-upgrade-path-for-a-directory-site.mdA src/content/posts/2026-05-19-the-one-line-security-fix-i-shipped-across-a-whole-fleet.mdA src/content/posts/2026-05-24-a-trust-badge-built-from-public-records.mdA src/content/posts/2026-05-26-nine-copies-of-the-same-function.mdA src/content/posts/2026-06-16-the-password-i-shipped-to-the-browser.mdA src/content/posts/2026-06-22-honest-ux-when-connected-doesnt-mean-can-post.mdA src/content/posts/2026-06-30-a-catalog-viewer-with-live-currency-conversion.mdA src/content/posts/2026-07-06-an-overnight-crawler-that-cost-zero-dollars.mdA src/content/posts/2026-07-13-taking-a-newsletter-product-live.mdA src/content/posts/2026-07-16-the-color-search-that-never-returns-an-empty-page.mdA src/content/posts/2026-07-17-a-send-button-that-refuses-to-send.mdA src/content/posts/2026-07-21-ingesting-two-million-property-parcels-from-open-data.mdA src/content/posts/2026-07-25-a-local-model-arena-for-picking-the-right-model.mdA src/content/posts/2026-07-28-the-guardrail-that-refused-my-own-command.md
Diff
commit be3a3f959231263b03ce536068bdf53ede208204
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jul 28 09:43:36 2026 -0700
Backfill Apr11-Jul28 journal gap: 16 generalized milestone posts + v2 content plan
All posts mapped to real shipped work, generalized (no vendor/client names,
no pricing/infra secrets) per the new content plan's no-trade-secrets rule.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
docs/plans/2026-07-28-content-plan.md | 94 ++++++++++++++++++++++
...8-one-template-a-family-of-niche-storefronts.md | 51 ++++++++++++
...30-retiring-a-bash-recipe-for-a-real-command.md | 55 +++++++++++++
...self-serve-upgrade-path-for-a-directory-site.md | 49 +++++++++++
...-security-fix-i-shipped-across-a-whole-fleet.md | 49 +++++++++++
...5-24-a-trust-badge-built-from-public-records.md | 48 +++++++++++
.../2026-05-26-nine-copies-of-the-same-function.md | 51 ++++++++++++
...-06-16-the-password-i-shipped-to-the-browser.md | 55 +++++++++++++
...onest-ux-when-connected-doesnt-mean-can-post.md | 49 +++++++++++
...catalog-viewer-with-live-currency-conversion.md | 49 +++++++++++
...-an-overnight-crawler-that-cost-zero-dollars.md | 50 ++++++++++++
.../2026-07-13-taking-a-newsletter-product-live.md | 56 +++++++++++++
...olor-search-that-never-returns-an-empty-page.md | 49 +++++++++++
...026-07-17-a-send-button-that-refuses-to-send.md | 48 +++++++++++
...-two-million-property-parcels-from-open-data.md | 54 +++++++++++++
...ocal-model-arena-for-picking-the-right-model.md | 47 +++++++++++
...28-the-guardrail-that-refused-my-own-command.md | 47 +++++++++++
17 files changed, 901 insertions(+)
diff --git a/docs/plans/2026-07-28-content-plan.md b/docs/plans/2026-07-28-content-plan.md
new file mode 100644
index 0000000..0e98970
--- /dev/null
+++ b/docs/plans/2026-07-28-content-plan.md
@@ -0,0 +1,94 @@
+# goodquestion.ai — Content Plan (v2)
+
+_Last updated: 2026-07-28_
+
+This replaces the ad-hoc posting cadence that let the journal go silent for weeks
+at a stretch (see the April 12 → May 6 gap). The blog is the public face of the
+"one founder + AI" story. It should read as an **active, honest build journal** —
+war stories, fixes, and lessons — not a marketing feed.
+
+## 1. Cadence
+
+- **Target: 3–5 posts/week.** Daily is not required and daily filler dilutes
+ quality. A strong post every couple of days beats seven thin ones.
+- **Every post maps to a real, shipped thing.** No speculative or aspirational
+ posts. If nothing shipped, don't post — the honesty is the brand.
+- **Backfill gaps in batches.** When the journal goes quiet, reconstruct the
+ missing milestones from the sources below rather than inventing days.
+
+## 2. Where posts come from (source of truth)
+
+1. **The wins log** (CNCP `http://localhost:3333/api/wins`) — one-line wins per
+ project per day. This is the richest, most current source (live since
+ 2026-07-22). Each cluster of related wins = one candidate post.
+2. **Git history across `~/Projects`** — for any window the wins log doesn't
+ cover, `git log --since --until` over the repos surfaces the real milestones.
+ Filter to feature/ship/launch commits; ignore wip/lint/merge noise.
+3. **Session recaps** — end-of-session summaries when available.
+
+A milestone is post-worthy when it has: a clear problem, a concrete fix, and a
+transferable lesson a business owner would care about.
+
+## 3. Generalize + no trade secrets (HARD RULE)
+
+Every post is public. Before publishing, strip anything proprietary:
+
+- **No brand/vendor names.** "a large product catalog," not the vendor.
+- **No private-label mappings** or any relationship between a label and its
+ upstream source.
+- **No pricing formulas, margins, cost bases, or MAP rules.**
+- **No client names** or identifying business details.
+- **No infra secrets** — IPs, hostnames, credentials, ports, internal domains,
+ DB names, tokens.
+- **No proprietary SKU/numbering schemes.**
+- **No detection-evasion / anti-bot technique detail.** Describe *that* a source
+ was hard to reach, not *how* it was bypassed.
+
+Generic scale numbers that already appear publicly (record counts, costs,
+durations, success rates) are fine — they make the story concrete without
+leaking anything.
+
+## 4. Post template
+
+```markdown
+---
+title: "Title Case, Specific, No Brand Names"
+description: "One sentence: the problem + the outcome."
+date: YYYY-MM-DD
+tags: ["3-6", "kebab-case", "topical", "founder-log"]
+---
+
+Opening hook — the concrete result or the surprise, in 2–3 sentences.
+
+## The Problem (or: The Setup)
+## The Fix
+## The Numbers (optional table when there are real metrics)
+## The Lesson
+## Why This Matters for Business Owners
+
+## Let's Connect
+<standard footer — @agentabrams links + advisory blurb>
+
+---
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day.
+This is what one founder + AI looks like.*
+```
+
+Text-only is the default. Only embed a video when a real capture exists in
+`/public/videos/` (broken media links look worse than no media).
+
+## 5. Voice
+
+First person. Honest about mistakes (the "what broke today" posts are the best
+ones). Lead with the concrete. End with a transferable lesson. Never overclaim.
+
+## 6. Backlog / recently backfilled (Apr 11 → Jul 28 2026)
+
+Filled the April 11 → July 28 silence with curated milestone posts, each mapped
+to a real shipped thing and generalized per §3: a CLI replacing a bash recipe,
+a fleet-wide one-line security fix, a nine-copies-of-one-function refactor, a
+credential-shipped-to-the-browser postmortem, honest live/simulated posting UX,
+a currency-aware catalog viewer, a zero-dollar overnight crawler, a newsletter
+go-live, an empty-result-proof color-search wheel, a draft-only send button,
+1.8M open-data property parcels, a local model arena, and a guardrail that
+refused an ambiguous command.
diff --git a/src/content/posts/2026-04-18-one-template-a-family-of-niche-storefronts.md b/src/content/posts/2026-04-18-one-template-a-family-of-niche-storefronts.md
new file mode 100644
index 0000000..bc21b66
--- /dev/null
+++ b/src/content/posts/2026-04-18-one-template-a-family-of-niche-storefronts.md
@@ -0,0 +1,51 @@
+---
+title: "One Template, a Family of Niche Storefronts"
+description: "How a single well-built template turned into dozens of distinct niche storefronts — and why 'distinct' turned out to be the hard part."
+date: 2026-04-18
+tags: ["frontend", "automation", "e-commerce", "scaling", "founder-log"]
+---
+
+I spent this stretch turning one storefront template into a whole family of niche sites — each one focused on a specific slice of a large catalog. The build itself was fast. The interesting problem was the opposite of what I expected: not "how do I make many sites," but "how do I keep them from all looking like the same site wearing different hats."
+
+## The Setup
+
+There's real demand for narrow, specific storefronts. A shopper searching for one particular material or style converts far better on a page that's *about that thing* than on a giant everything-catalog. So the play is obvious: take the products that match a niche, wrap them in a focused site, and let each one rank and convert on its own terms.
+
+The catalog and the product data already existed. The template — grid, filters, product pages, contact flow — already existed. Multiplying them is trivial. That's exactly the trap.
+
+## The Problem
+
+If you stamp the same template out fifty times with only the product list swapped, search engines notice. Near-identical pages across many domains read as "doorway pages," and the whole family gets discounted at once. Worse, it's just bad — a shopper who lands on two of them in a row immediately clocks that they're the same machine.
+
+So each site needed its own visual identity: palette, typography, hero treatment, tone — something that matched the *name and subject* of that niche rather than a shared house style.
+
+## The Fix
+
+I treated the visual identity as data, not as hand-work. Each site gets a small config that drives its palette and type choices, keyed off the aesthetic of its subject. The shared template reads that config; the shared code stays shared, but the *rendered* result diverges enough to feel purpose-built. Heroes are drawn from products that actually belong to that niche, and I run an audit pass that flags any two sites drifting back toward looking the same.
+
+The discipline that made it work: **the template is shared, the identity is not.** Bug fixes and features flow to every site from one place. Look-and-feel is per-site and deliberately divergent.
+
+## The Lesson
+
+The easy 80% of "build many things from one thing" is the templating. The 20% that actually determines whether it works is making each instance earn its own existence. If the instances aren't meaningfully different, you haven't built fifty things — you've built one thing and diluted it fifty ways.
+
+## Why This Matters for Business Owners
+
+Narrow beats broad for discovery. A focused page for a specific need will out-convert a general catalog almost every time. But "spin up a bunch of landing pages" only works if each one is genuinely distinct — otherwise you're spending effort to make yourself *less* visible, not more. Build the machine that makes them different, not just the machine that makes them fast.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-04-30-retiring-a-bash-recipe-for-a-real-command.md b/src/content/posts/2026-04-30-retiring-a-bash-recipe-for-a-real-command.md
new file mode 100644
index 0000000..f0b4f52
--- /dev/null
+++ b/src/content/posts/2026-04-30-retiring-a-bash-recipe-for-a-real-command.md
@@ -0,0 +1,55 @@
+---
+title: "Retiring a Bash Recipe for a Real Command"
+description: "A fragile copy-paste bash recipe kept causing the same mistakes, so I turned it into a first-class CLI subcommand — and the errors stopped."
+date: 2026-04-30
+tags: ["developer-tools", "cli", "automation", "reliability", "founder-log"]
+---
+
+Some of the most valuable work isn't a shiny feature — it's taking a fragile thing everyone tolerates and making it solid. Today that was a bash "recipe": a block of shell that lived in the docs, that you were supposed to copy, paste, and edit before running. I replaced it with a real command.
+
+## The Problem
+
+The recipe worked, technically. But it had all the failure modes copy-paste instructions always have:
+
+- People edited the wrong line, or forgot to edit a line.
+- It silently assumed things about the environment that weren't always true.
+- When it failed, it failed in the middle, leaving a half-done mess.
+- There was no validation — it would happily run against bad input and produce bad output.
+
+Every one of those had bitten me at least once. A recipe is documentation pretending to be a tool, and it fails exactly when you're moving fast.
+
+## The Fix
+
+I promoted it to a proper subcommand in the CLI. Same underlying steps, but now:
+
+- **Arguments are parsed and validated** up front. Bad input gets a clear error before anything runs, not a cryptic failure three steps in.
+- **It's idempotent-aware** — it knows what "already done" looks like and doesn't redo or double-apply.
+- **Failures are actionable.** Instead of a stack trace, you get a message that tells you what's wrong and what to do about it.
+- **Secrets are redacted** in any output it prints, so nothing sensitive leaks into logs or a paste.
+
+While I was in there I added a couple of adjacent guardrails the recipe never had — detecting a category of naming collision that used to produce confusing results, and giving the diagnostic command the ability to actually probe whether credentials work instead of just checking that they're present.
+
+## The Lesson
+
+**A recipe is a bug report waiting to happen.** Any procedure that lives as "copy this, edit that, run it" is accumulating a tax you pay every time you use it — and a bigger one every time you use it wrong. The moment a recipe is run more than a handful of times, it's cheaper to make it a command. The command can validate, refuse bad input, and fail loudly instead of halfway.
+
+## Why This Matters for Business Owners
+
+Your team's tribal knowledge — the "oh, you just run this and change these two things" steps — is invisible risk. It works right up until the day someone changes the wrong thing at the worst time. Turning those recipes into real tools with validation and clear errors is unglamorous, cheap with AI assistance, and it quietly removes a whole class of "how did *that* happen" incidents.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-05-12-a-self-serve-upgrade-path-for-a-directory-site.md b/src/content/posts/2026-05-12-a-self-serve-upgrade-path-for-a-directory-site.md
new file mode 100644
index 0000000..721d77a
--- /dev/null
+++ b/src/content/posts/2026-05-12-a-self-serve-upgrade-path-for-a-directory-site.md
@@ -0,0 +1,49 @@
+---
+title: "A Self-Serve Upgrade Path for a Directory Site"
+description: "A local-business directory had listings but no way to monetize them, so I added a tier ladder and a marketplace board — the parts that turn a directory into a business."
+date: 2026-05-12
+tags: ["product", "monetization", "directory", "small-business", "founder-log"]
+---
+
+A directory of local businesses is useful the moment it exists. But "useful" and "a business" are different things. Today I added the two pieces that bridge that gap: a self-serve upgrade path and a local listings marketplace.
+
+## The Setup
+
+The directory already had the hard part done — real, structured data about a lot of local businesses, presented cleanly. What it didn't have was any way for a business to *do* something about its own listing, or any reason for the directory to earn money.
+
+A directory with no upgrade path is a favor you're doing for other people. That's fine as a loss leader, but it doesn't compound.
+
+## The Fix
+
+Two additions, both deliberately simple:
+
+1. **A tier ladder with intent capture.** Each business's page now has an upgrade path — a clear ladder of what a claimed or enhanced listing gets you, with a way to express interest right there. The key design choice was capturing *intent* first rather than forcing a full checkout: I'd rather know who wants what than lose them at a payment wall on day one.
+
+2. **A local listings marketplace board.** A place where relevant local listings surface in one board, giving both visitors and businesses a second reason to come back. It turns the directory from a static reference into something with fresh, changing content.
+
+Neither of these is technically fancy. The value is that they change what the site *is* — from a read-only reference into a two-sided surface where the businesses in it have a reason to engage.
+
+## The Lesson
+
+**Ship the monetization surface early, even before you're ready to charge.** Intent capture costs almost nothing to build and tells you whether there's demand before you invest in billing, fulfillment, or support. If nobody clicks the upgrade path, that's the cheapest possible signal that the offer is wrong. If they do, you've got a warm list to build the real flow around.
+
+## Why This Matters for Business Owners
+
+A lot of "content" assets — directories, guides, tools you give away — sit there generating goodwill and nothing else. The move isn't to slap ads on them; it's to add a low-friction way for the people who already find them valuable to raise their hand and pay for more. Build the "I want more" button before you build the vending machine behind it. The button tells you whether the vending machine is worth building.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-05-19-the-one-line-security-fix-i-shipped-across-a-whole-fleet.md b/src/content/posts/2026-05-19-the-one-line-security-fix-i-shipped-across-a-whole-fleet.md
new file mode 100644
index 0000000..6ae5a3d
--- /dev/null
+++ b/src/content/posts/2026-05-19-the-one-line-security-fix-i-shipped-across-a-whole-fleet.md
@@ -0,0 +1,49 @@
+---
+title: "The One-Line Security Fix I Shipped Across a Whole Fleet"
+description: "Every external link on every site was missing one small attribute. Fixing it once is trivial; fixing it everywhere, consistently, is the actual work."
+date: 2026-05-19
+tags: ["security", "frontend", "automation", "web", "founder-log"]
+---
+
+Today's win was boring in the best way: I added `rel="noopener noreferrer"` to every external link across a whole fleet of sites. One tiny attribute, everywhere. The lesson isn't the attribute — it's what "everywhere, consistently" actually takes.
+
+## The Problem
+
+When a page opens a link in a new tab with `target="_blank"`, the page it opens gets a handle back to your page through `window.opener` — unless you explicitly cut that handle. That handle lets the destination do things it has no business doing, and it can leak referrer information you didn't mean to share. The fix has been standard practice for years: add `rel="noopener noreferrer"` to those links.
+
+I had it on *some* links, on *some* sites. Which is almost worse than not knowing — it creates a false sense that it's handled.
+
+## The Fix
+
+The temptation is to open each site and fix links by hand. That's how you end up with "some links on some sites" all over again. Instead I treated it as a fleet-wide sweep:
+
+- **Find every offender programmatically** — every `target="_blank"` without the `rel` guard — across all the sites at once.
+- **Apply the same fix everywhere**, additively, so nothing else changes.
+- **Verify by re-scanning**, not by eyeballing. The proof that it's done is that a second scan finds zero remaining offenders — not that I remember fixing them.
+
+Same day, adjacent hygiene from the same mindset: killing the endless `/favicon.ico` 404 on sites that didn't have one, and tightening the ignore rules so stray backup files stop sneaking into commits.
+
+## The Lesson
+
+**"Fix it once" and "fix it everywhere" are different projects.** The first is a code change. The second is a *process*: enumerate every instance mechanically, apply one change uniformly, and prove completeness by re-running the enumeration. If your evidence that a fleet-wide fix is done is your own memory, it isn't done. The scan that finds zero is the receipt.
+
+## Why This Matters for Business Owners
+
+Security and quality debt in a portfolio of sites hides in the gap between "we fixed that" and "we fixed that everywhere." The dangerous state isn't ignorance — it's partial coverage that feels like completion. Whenever you apply a fix across more than one property, the deliverable isn't the fix; it's the clean re-scan that proves no instance was missed. Insist on the receipt.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-05-24-a-trust-badge-built-from-public-records.md b/src/content/posts/2026-05-24-a-trust-badge-built-from-public-records.md
new file mode 100644
index 0000000..98b6c61
--- /dev/null
+++ b/src/content/posts/2026-05-24-a-trust-badge-built-from-public-records.md
@@ -0,0 +1,48 @@
+---
+title: "A Trust Badge Built From Public Records"
+description: "A directory got a lot more useful the day it started telling visitors which listed businesses might be operating without a license — using only public data."
+date: 2026-05-24
+tags: ["data", "directory", "trust", "public-records", "founder-log"]
+---
+
+I shipped a small badge today that punches well above its weight: on a local-business directory, certain listings now carry a "possibly no business license" flag. It's built entirely from public records, and it turns a plain listing into something a visitor can actually make a decision with.
+
+## The Setup
+
+A directory that just lists businesses is a phone book. The value isn't the list — it's the *signal* layered on top of it. Anyone can tell you a business exists. What people actually want to know is: should I trust this one?
+
+Business-license status is exactly that kind of signal. It's public information, but it's scattered, unglamorous, and nobody cross-references it against a directory listing on their own. So doing that cross-reference *for* the visitor is real value.
+
+## The Fix
+
+The badge is deliberately careful in its wording — "possibly," not "definitely." That single word carries a lot of weight:
+
+- The public data is authoritative about what it contains, but silence in a dataset isn't proof of absence. A business might be licensed under a slightly different name, or in a way the public record doesn't cleanly surface.
+- So the badge signals *worth checking*, not *guilty*. It points the visitor at a question to ask, not a verdict to act on.
+
+Mechanically it's a match between the directory's businesses and the public license records, with the flag raised when a business has no clear corresponding record. The engineering is ordinary. The care is in the framing.
+
+## The Lesson
+
+**Derived signals are where directories earn their keep — and honesty about uncertainty is what makes them safe to ship.** A badge that overclaims ("UNLICENSED") would be both wrong sometimes and legally reckless. A badge that hedges precisely ("possibly — worth verifying") is useful *and* defensible. The engineering was the easy part; getting the certainty language right was the part that mattered.
+
+## Why This Matters for Business Owners
+
+You almost certainly have data that, combined with a public dataset, produces a signal your customers would value. The instinct is to state signals as facts because facts sound more impressive. Resist it. A calibrated signal — one that's honest about what it does and doesn't know — builds trust every time it's right and survives every time it's wrong. An overconfident one destroys trust the first time it's wrong, and it always eventually is.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-05-26-nine-copies-of-the-same-function.md b/src/content/posts/2026-05-26-nine-copies-of-the-same-function.md
new file mode 100644
index 0000000..7ee3866
--- /dev/null
+++ b/src/content/posts/2026-05-26-nine-copies-of-the-same-function.md
@@ -0,0 +1,51 @@
+---
+title: "Nine Copies of the Same Function"
+description: "The same HTML-escaping helper had been pasted into nine files. Deduping it — and adding the first real test suite — was the highest-leverage hour of the day."
+date: 2026-05-26
+tags: ["refactoring", "code-quality", "testing", "maintainability", "founder-log"]
+---
+
+Today I went looking for one bug and found a structural one instead: the same little HTML-escaping function had been copy-pasted into nine different files. Nine. That's not a bug yet — but it's a bug generator, and it was worth stopping to fix.
+
+## The Problem
+
+Escaping user-supplied text before it hits HTML is security-critical — it's how you stop content from becoming code. So the *last* thing you want is nine slightly-different copies of that logic drifting apart over time.
+
+The danger with duplicated logic isn't that it's ugly. It's that the copies diverge. You fix a bug in one, forget the other eight. You harden one against a new edge case, leave the rest soft. Eventually "the escape function" isn't one thing with one behavior — it's nine things that happen to share a name, and you can't reason about any of them.
+
+## The Fix
+
+Two moves, in order:
+
+1. **Collapse the nine into one.** I extracted a single shared helper and pointed every call site at it. Now there's exactly one place escaping happens, one place to fix it, one behavior to reason about. I did the same for a set of structured-data builders that had similar copy-paste sprawl.
+
+2. **Add the safety net first.** Before and around the refactor, I stood up a real smoke-test suite — a few dozen fast tests that assert the important behaviors. That's what makes a refactor safe: the tests are the thing that tells you the "no behavior change" refactor actually didn't change behavior.
+
+Order matters. The tests aren't a victory lap after the refactor — they're the permission slip to do it at all.
+
+## The Lesson
+
+**Duplication is a slow leak, and the fix is cheap while it's still small.** Nine copies is annoying; nine *divergent* copies is a security incident waiting for the wrong Tuesday. The moment you notice the same logic in more than a couple of places — especially security-relevant logic — that's the signal to consolidate, while it's still a mechanical change and not an archaeology project.
+
+And you consolidate behind tests, not vibes. A refactor without a safety net is just moving code and hoping.
+
+## Why This Matters for Business Owners
+
+"It works, don't touch it" is how small, invisible risks compound into expensive ones. Duplicated critical logic is the classic example: everything's fine until the day one copy gets a fix the others don't, and now you have inconsistent behavior nobody can explain. Paying down that debt while it's tiny — one shared function, one test suite — is far cheaper than the incident it prevents. With AI assistance, an hour of this pays for itself many times over.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-06-16-the-password-i-shipped-to-the-browser.md b/src/content/posts/2026-06-16-the-password-i-shipped-to-the-browser.md
new file mode 100644
index 0000000..14b50ac
--- /dev/null
+++ b/src/content/posts/2026-06-16-the-password-i-shipped-to-the-browser.md
@@ -0,0 +1,55 @@
+---
+title: "The Password I Shipped to the Browser"
+description: "A dashboard was printing its own auth password into client-side code. Here's how I found it, why it happened, and the canary I added so a dead token never goes unnoticed again."
+date: 2026-06-16
+tags: ["security", "secrets", "monitoring", "postmortem", "founder-log"]
+---
+
+Two security fixes today, and I'm going to be honest about the first one because the honesty is the point: a dashboard was shipping its own auth password into the browser. My mistake. Found it, fixed it, and then built the thing that would have caught it.
+
+## What Happened
+
+The dashboard was protected by basic auth. Somewhere in its startup/error handling, the password ended up embedded in the client-side code — the stuff that gets sent to every visitor's browser. A secret-scanning tool flagged it. Anything shipped to the browser is public; there is no "but you'd have to look" — view-source is one keystroke.
+
+How does a password end up in browser code? The usual way: a helpful error message. Some boot-error path was constructed with the config values inline "to make debugging easier," and the config included the credential. It was convenient exactly until it was a leak.
+
+## The Fix
+
+Two parts, both non-negotiable:
+
+1. **Remove the credential from anything that reaches the client**, and **rotate it.** A leaked secret is a dead secret — you don't un-leak it, you replace it. So the old one is gone, not just hidden.
+2. **The credential lives server-side only**, injected at runtime, never baked into shipped code.
+
+## The Better Fix: A Canary
+
+The more valuable outcome today was the second one. A different system had a token that could silently expire — and when it did, a whole panel of the product just quietly went dark. No error, no alert. It looked "connected" while doing nothing.
+
+So I built a token-health canary: it periodically checks that the token is actually alive, warns before it's about to expire, and stamps a heartbeat every run. If the token dies — or if the canary itself stops running — I get told, instead of discovering it weeks later because something looked off.
+
+## The Lesson
+
+Two lessons, really:
+
+- **Convenience is the most common cause of leaks.** Nobody sets out to ship a password. They add a debug string, or an error message with "helpful" context, and the secret rides along. Assume anything that reaches the browser is public, and never construct client-facing strings from config that might contain secrets.
+- **Silent failure is worse than loud failure.** A token that dies quietly is more dangerous than one that dies with an alarm, because you build on the assumption it's working. Every critical credential should have something watching that it's alive — and something watching the watcher.
+
+## Why This Matters for Business Owners
+
+The scary security problems usually aren't dramatic breaches — they're small, quiet lapses. A secret that leaked through a convenience. A connection that died without telling anyone. Two cheap habits prevent most of the damage: scan for secrets automatically so a leak gets caught the day it happens, and put a heartbeat on anything whose silent death would hurt. Both are inexpensive. Both save you from the class of problem you only discover long after it started.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-06-22-honest-ux-when-connected-doesnt-mean-can-post.md b/src/content/posts/2026-06-22-honest-ux-when-connected-doesnt-mean-can-post.md
new file mode 100644
index 0000000..691dd27
--- /dev/null
+++ b/src/content/posts/2026-06-22-honest-ux-when-connected-doesnt-mean-can-post.md
@@ -0,0 +1,49 @@
+---
+title: "Honest UX: When 'Connected' Doesn't Mean 'Can Post'"
+description: "A social dashboard showed accounts as 'connected' — but connected and actually-able-to-post are different things, and the gap was quietly eating posts."
+date: 2026-06-22
+tags: ["ux", "product", "social", "reliability", "founder-log"]
+---
+
+I spent today making a social-posting dashboard tell the truth. It already showed each account as "connected." The problem was that "connected" and "can actually post right now" turned out to be two different facts — and conflating them meant some posts silently went nowhere.
+
+## The Problem
+
+The dashboard had a green "connected" indicator per account. It felt done. But "connected" only meant *we have a stored credential for this account*. It said nothing about whether that credential still had the right permissions, whether the platform was currently accepting posts through it, or whether the whole thing was actually wired up versus running in a simulated mode for testing.
+
+So you'd queue a post, the account looked connected, and — nothing. Or worse, the UI cheerfully reported success while the post never left the building. The interface was optimistic in a way the reality didn't support.
+
+## The Fix
+
+I split the one comforting green light into honest, separate states:
+
+- **A real "postable" status** — not "we have a credential," but "this account can actually accept a post right now." Those are different checks, and only the second one matters to the person hitting publish.
+- **Truthful live-vs-simulated labeling.** If something is running in a test/simulated mode, the UI says so plainly instead of looking identical to the real thing. Nobody should have to guess whether a "success" was real.
+- **An approval pipeline with honest feedback** — so a post moves through visible states you can trust, and a failure looks like a failure.
+
+The theme across all of it: the UI now refuses to imply more certainty than it has.
+
+## The Lesson
+
+**A status indicator that's optimistic by default is a liar by default.** The most dangerous UI state isn't "error" — it's "looks fine, isn't." When a green light can mean "definitely working" *or* "we haven't actually checked," people learn to trust it, and then it burns them. The fix is to make the interface report exactly what it knows and no more: connected is not authorized, authorized is not working, and simulated is never shown as live.
+
+## Why This Matters for Business Owners
+
+Dashboards are supposed to reduce uncertainty. A dashboard that overstates its confidence does the opposite — it manufactures false calm, and false calm is expensive because you act on it. If a tool tells you something shipped, you should be able to bet on it. Demand interfaces that distinguish "we tried" from "it worked," and that never dress up a simulation as the real thing. Honest UX is slower to build and far cheaper to live with.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-06-30-a-catalog-viewer-with-live-currency-conversion.md b/src/content/posts/2026-06-30-a-catalog-viewer-with-live-currency-conversion.md
new file mode 100644
index 0000000..9009aa4
--- /dev/null
+++ b/src/content/posts/2026-06-30-a-catalog-viewer-with-live-currency-conversion.md
@@ -0,0 +1,49 @@
+---
+title: "A Catalog Viewer With Live Currency Conversion"
+description: "Building a product-catalog viewer for an overseas line meant solving prices in a foreign currency, color-matched navigation, and finding a pattern's sibling colorways."
+date: 2026-06-30
+tags: ["e-commerce", "frontend", "data", "internationalization", "founder-log"]
+---
+
+This one was about making a large catalog from an overseas source *browsable* — not just importable. The data was in a foreign currency, organized by the source's conventions instead of a shopper's, and full of patterns that came in many colorways with no easy way to see them together. I built the viewer that fixes all three.
+
+## The Problem
+
+Importing overseas product data is the easy 20%. The hard 80% is that the data is shaped for the source, not for a buyer:
+
+- **Prices are in a foreign currency.** A number a shopper can't instantly translate is a number they bounce off of.
+- **Navigation follows the source's taxonomy**, which rarely matches how a buyer actually shops (by use, by width, by performance, by color).
+- **A single pattern exists in many colorways**, but they're scattered — there's no "show me the other colors of this one."
+
+## The Fix
+
+Three features, each aimed at one of those gaps:
+
+1. **Live currency conversion on load.** Prices render converted to the shopper's currency the moment the page loads, using a current rate — so a price is a price, not a puzzle.
+2. **Shopper-shaped facets.** I added navigation by the dimensions people actually filter on, layered on top of the source's raw categories. The buyer never has to learn the vendor's internal taxonomy.
+3. **Colorway sibling chips.** On any product, chips surface the *other* colorways of that same pattern — one tap to see the design in every color it comes in. This is the feature that turns "a wall of products" into "a collection you can explore."
+
+## The Lesson
+
+**The gap between 'imported' and 'shoppable' is where all the real work lives.** Getting the data in is a script. Making it feel native to the person browsing — right currency, right filters, related items grouped — is a series of deliberate product decisions, each one closing a small "wait, how do I…" that would otherwise cost you the visitor. Nobody thanks you for the import. They stay because of the browsing experience on top of it.
+
+## Why This Matters for Business Owners
+
+If you sell products sourced from somewhere else — overseas suppliers, distributors, aggregated feeds — the raw data is never in a shape your customers will tolerate. Budget for the *presentation* layer as its own project, not as a footnote to the import. Currency, sensible filtering, and grouping related items aren't polish; they're the difference between a catalog people buy from and a spreadsheet with pictures.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-07-06-an-overnight-crawler-that-cost-zero-dollars.md b/src/content/posts/2026-07-06-an-overnight-crawler-that-cost-zero-dollars.md
new file mode 100644
index 0000000..db46622
--- /dev/null
+++ b/src/content/posts/2026-07-06-an-overnight-crawler-that-cost-zero-dollars.md
@@ -0,0 +1,50 @@
+---
+title: "An Overnight Crawler That Cost Zero Dollars"
+description: "I needed to harvest full catalogs from 25 sources and analyze every image — so I built a resumable overnight loop that runs on structured feeds and local models, for nothing."
+date: 2026-07-06
+tags: ["automation", "scraping", "cost-optimization", "ai", "founder-log"]
+---
+
+I woke up to 25 fully-harvested catalogs and color-analyzed images across all of them. The whole run cost me zero dollars in API fees, and if it had been interrupted at 3am it would have picked up exactly where it left off. Here's how I built an overnight harvest that's both free and un-babysittable.
+
+## The Problem
+
+Harvesting a lot of catalogs has two cost centers that quietly add up:
+
+1. **The fetching** — if you drive a full headless browser against every page of every site, it's slow, fragile, and heavy.
+2. **The analysis** — if you send every product image to a paid vision API, the bill scales linearly with your catalog and you're one runaway loop away from a nasty invoice.
+
+And a job that runs for hours unattended has a third problem: if it dies partway, restarting from zero wastes everything it already did.
+
+## The Fix
+
+Three decisions made it cheap and reliable:
+
+- **Feed-first fetching.** Most e-commerce sites expose their catalog as structured data if you know where to look — a products feed, a sitemap, an underlying data endpoint. Reading the structured feed instead of scraping the rendered page is faster, sturdier, and doesn't need a browser at all. When a feed exists, that's the door.
+- **Local models for analysis.** Instead of a paid vision API, image color identification runs on a local model on my own hardware. It's not billed per call, so analyzing thousands of images costs the same as analyzing ten: nothing.
+- **A resumable loop.** The job tracks what it's already done and checks that before doing anything. Interrupt it, restart it, run it again tomorrow — it does only the remaining work. That's what makes "leave it running overnight" actually safe.
+
+## The Lesson
+
+**Cost and reliability come from architecture, not effort.** The expensive, fragile version of this job and the free, robust version do the same thing — the difference is entirely in the choices: read the feed instead of the page, run the model locally instead of renting it, and make every step resumable so an interruption costs minutes, not a whole run. None of those are harder to build. They're just the ones you pick when you've been burned before.
+
+## Why This Matters for Business Owners
+
+"AI automation" doesn't have to mean a per-call meter running against you all night. A huge amount of real work — harvesting data, analyzing images, enriching records — can run on structured sources and local models for effectively nothing. Before you sign up for usage-based pricing on a job that scales with your data, ask whether it can run on a feed and a local model instead. Often it can, and the difference is a rounding error versus a real monthly bill.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-07-13-taking-a-newsletter-product-live.md b/src/content/posts/2026-07-13-taking-a-newsletter-product-live.md
new file mode 100644
index 0000000..e0aff64
--- /dev/null
+++ b/src/content/posts/2026-07-13-taking-a-newsletter-product-live.md
@@ -0,0 +1,56 @@
+---
+title: "Taking a Newsletter Product Live"
+description: "Shipping a scheduled-digest newsletter to real subscribers meant scheduled sends, honest copy, runtime-injected credentials, and two clean verification sweeps before go-live."
+date: 2026-07-13
+tags: ["launch", "email", "automation", "verification", "founder-log"]
+---
+
+Today I took a newsletter product from "works on my machine" to "sends real digests to real subscribers on a schedule." Go-live days are where all the shortcuts you took come due. Here's the checklist that got it live cleanly.
+
+## The Setup
+
+The product turns public data into a periodic digest that subscribers actually want in their inbox. The content pipeline already worked. Going live meant everything *around* the content had to be trustworthy: it had to send on a schedule without me, use credentials safely, say true things on the page and in the emails, and be verified — not just assumed — before a single real subscriber got a message.
+
+## The Fix
+
+The go-live broke into a few concrete pieces:
+
+- **Scheduled sends.** A scheduler fires the digest at fixed times, morning and evening, with no human in the loop. The whole point of a newsletter is that it goes out whether or not I'm awake.
+- **Runtime credential injection.** The mail-sending credentials are injected at runtime, never baked into the code or committed. Same principle as any secret: it lives in the environment, not the repo.
+- **Honest copy everywhere.** I went through the page and the emails and made the claims match reality — no "live" language on things that weren't, links forced to secure URLs only, proper preview/social tags so shared links look right.
+- **An access wall** in front of the admin surfaces, with the machine-to-machine paths (the payment webhook, the import token) deliberately exempted so automation still flows.
+
+## The Numbers
+
+The part I won't skip: verification. I ran the whole thing through repeated end-to-end sweeps — request, render, interact — fixing anything each sweep caught, until it came back clean twice in a row.
+
+| Check | Result |
+|-------|--------|
+| Verification sweeps to clean | 2 consecutive, 0 defects |
+| Scheduled digests | morning + evening, automated |
+| Credentials in code | none (runtime-injected) |
+
+## The Lesson
+
+**"It works" is a demo. "It works unattended, on a schedule, verified twice" is a launch.** The gap between those two is entirely the unglamorous stuff — scheduling, secrets, honest copy, access control — and it's exactly the stuff that, skipped, turns into a 6am incident with real users watching. Two clean sweeps before go-live is cheap. One embarrassing send to your whole list is not.
+
+## Why This Matters for Business Owners
+
+Anything that talks to your customers automatically — email, SMS, notifications — is a promise that runs without supervision. Before you flip it on, the questions that matter aren't about features; they're operational: Does it run on its own? Are the credentials safe? Does every claim it makes match reality? Has it been verified end-to-end, twice, or just tried once? Insist on the boring checklist. It's the difference between a launch and an apology email.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-07-16-the-color-search-that-never-returns-an-empty-page.md b/src/content/posts/2026-07-16-the-color-search-that-never-returns-an-empty-page.md
new file mode 100644
index 0000000..c820cbf
--- /dev/null
+++ b/src/content/posts/2026-07-16-the-color-search-that-never-returns-an-empty-page.md
@@ -0,0 +1,49 @@
+---
+title: "The Color-Search Wheel That Never Returns an Empty Page"
+description: "A shop-by-color feature is great until a color returns zero products. Here's the tunable-tolerance-plus-fallback design that guarantees a shopper always sees results."
+date: 2026-07-16
+tags: ["e-commerce", "search", "ux", "algorithms", "founder-log"]
+---
+
+Shop-by-color is one of those features that delights shoppers — until they pick a color and get an empty page. Today I fixed the empty-page failure mode with two ideas: a tunable tolerance band, and a guarantee that you always get *some* results.
+
+## The Problem
+
+A color wheel lets a shopper click a hue and see matching products. The naive version matches products whose color is *close* to the clicked color — where "close" is some fixed distance threshold. That works great for popular colors with lots of matches. It fails badly for anything in a sparse part of the spectrum: click an unusual hue, and the fixed threshold catches nothing. Empty page. Dead end. The shopper assumes you have nothing and leaves.
+
+An empty result on a browse feature is worse than no feature at all, because it reads as "this store is thin."
+
+## The Fix
+
+Two mechanisms, working together:
+
+1. **A tunable tolerance band.** Instead of a hardcoded "how close counts as a match," the band is a parameter (defaulting to a sensible width). Widen it and the wheel is more forgiving; narrow it for precision. Making it tunable meant I could actually reason about the tradeoff instead of guessing one magic number forever.
+
+2. **A nearest-neighbor guarantee.** The important one. You can ask the search to *always* return at least N results. If the tolerance band is too thin to fill that quota, it falls back to the nearest matches by color distance — the closest products to what you clicked, even if they're outside the strict band. The band controls *quality*; the fallback controls *never-empty*.
+
+So a shopper picking a rare color still sees the closest things you have, ranked by how close they are, instead of a blank grid.
+
+## The Lesson
+
+**Design for the sparse case, because that's where features die.** Anything that filters — search, color match, recommendations — is easy to get right in the dense middle and easy to get wrong in the thin tails. And the tails are exactly where a curious shopper wanders. A fixed threshold optimizes for the common case and abandons the edge cases; a tolerance band plus a "always return the nearest N" fallback serves both. The guarantee that there's *always* a next thing to look at is what keeps someone browsing.
+
+## Why This Matters for Business Owners
+
+Every "explore" feature you add is a promise that exploring leads somewhere. The fastest way to break that promise is a dead end — a filter combination or a search that returns nothing. Whenever you build browse-and-discover, ask what happens at the edges, not just the middle: what does a shopper see when their pick is rare? "The closest things we've got" keeps them engaged. A blank page sends them to a competitor.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-07-17-a-send-button-that-refuses-to-send.md b/src/content/posts/2026-07-17-a-send-button-that-refuses-to-send.md
new file mode 100644
index 0000000..81e2aae
--- /dev/null
+++ b/src/content/posts/2026-07-17-a-send-button-that-refuses-to-send.md
@@ -0,0 +1,48 @@
+---
+title: "A Send Button That Refuses to Send"
+description: "The safest outbound-email button I've built doesn't send anything — it creates a draft and stops. Here's why that constraint is a feature, not a limitation."
+date: 2026-07-17
+tags: ["safety", "automation", "email", "product", "founder-log"]
+---
+
+Today I shipped a "Send via email" button whose entire job is to *not* send. Click it and it creates a draft in the mailbox — fully composed, ready to go — and then it stops and waits for a human to hit send. That constraint is deliberate, and it's one of my favorite patterns for automation that touches the outside world.
+
+## The Problem
+
+Automation that sends things to real people is a different risk class than automation that stays inside your own systems. A data pipeline that misfires wastes some compute. An outbound email that misfires reaches a customer, can't be recalled, and can damage a relationship or a reputation. "Undo" doesn't exist once it's sent.
+
+The tempting design is a one-click "send it" that composes and fires in one motion. It feels efficient. It's also exactly how a bug, a bad template, or a wrong recipient list becomes an irreversible mistake at machine speed.
+
+## The Fix
+
+I split "compose" from "send," and only automated the reversible half:
+
+- The button does all the tedious work — pulls the right context, writes the message, addresses it — and **saves it as a draft.**
+- A human opens the draft, reads it, and sends it (or doesn't). The irreversible action stays in human hands.
+
+The automation removes all the friction that makes people avoid the task, while keeping the one step that must not be automated: the moment it becomes irreversible and public.
+
+## The Lesson
+
+**Automate up to the point of no return, then stop.** The highest-value, lowest-risk place to draw the line in any outbound workflow is right before the irreversible, outward-facing action. Everything upstream of that — gathering, drafting, formatting — is safe to hand to a machine and is usually the boring part anyway. The send itself is one click for a human, and that one click is a meaningful safety gate, not a bottleneck. A draft-only button is faster than doing it by hand and safer than full automation. You rarely get both.
+
+## Why This Matters for Business Owners
+
+When you automate anything customer-facing — emails, posts, messages, charges — the question isn't just "can we automate it," it's "what happens when it's wrong, and can we take it back?" For reversible steps, automate freely. For the irreversible, outward-facing moment, keep a human in the loop by design. A tool that drafts everything and lets a person send is the sweet spot: it kills the drudgery without handing a machine the power to make a public mistake you can't unmake.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-07-21-ingesting-two-million-property-parcels-from-open-data.md b/src/content/posts/2026-07-21-ingesting-two-million-property-parcels-from-open-data.md
new file mode 100644
index 0000000..a7de18a
--- /dev/null
+++ b/src/content/posts/2026-07-21-ingesting-two-million-property-parcels-from-open-data.md
@@ -0,0 +1,54 @@
+---
+title: "Ingesting Two Million Property Parcels From Open Data"
+description: "A single county's open-data portal has nearly two million property records. Getting them in — cleanly, with search that actually finds an address — was the real challenge."
+date: 2026-07-21
+tags: ["data", "public-records", "real-estate", "search", "founder-log"]
+---
+
+Today I ingested nearly **1.9 million property parcels** for a single large county — owners, mailing addresses, assessed values, and physical details — straight from the county's own open-data portal. The download is easy. Making almost two million records searchable, and making search actually find the address you typed, is where the day went.
+
+## The Setup
+
+Property data is some of the richest public data there is, and a lot of it is genuinely open — counties publish parcel records through open-data portals. But "published" and "usable" are a long way apart. The raw export is huge, inconsistently formatted, and organized for records-keeping, not for a person typing an address into a box.
+
+## The Fix
+
+A few problems had to be solved to go from a giant file to a working feature:
+
+- **Streaming, not slurping.** You can't load a mult-million-row file into memory and hope. The parser streams the data through so memory stays flat regardless of file size. This is the difference between an import that finishes and one that dies.
+- **Honest labeling of what's there.** Assessed values come with a stated basis, and I carried that label through rather than presenting a number without its context. Some jurisdictions withhold certain fields entirely — where that's the case, the data says "absent," not zero. Never dress up a gap as a value.
+- **Address search that forgives humans.** This was the subtle one. People don't type addresses the way the records store them. Someone searches "350 5th Ave"; the record says "338 5 AVENUE" for the same lot. So search normalizes ordinals ("5th" → "5"), and when there's no exact house-number hit, it falls back to the nearest number on the street. The famous building at that address now resolves even though the typed number and the stored number don't match.
+
+## The Numbers
+
+| Metric | Value |
+|--------|-------|
+| Parcels ingested (one county) | ~1.9 million |
+| Residential records with detail | ~1.1 million |
+| Memory profile | flat (streaming parser) |
+| Withheld fields | labeled absent, not faked |
+
+## The Lesson
+
+**Open data is a raw material, not a product.** The value isn't in having the file — anyone can download it. It's in the ingestion that survives the scale, the honesty that labels what's missing instead of faking it, and the search that meets a human where they actually type. Those three are the whole job. The download is the part that fits in a tweet; the rest is the part that makes it worth anything.
+
+## Why This Matters for Business Owners
+
+There is an enormous amount of valuable public data sitting in government portals, free for anyone willing to do the unglamorous work of making it usable. That work — robust ingestion, honest handling of gaps, human-friendly search — is exactly what AI-assisted development is good at, and it's a real moat: your competitors could download the same file and most of them won't do the 80% that makes it useful. The data is free. The usefulness is the product.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-07-25-a-local-model-arena-for-picking-the-right-model.md b/src/content/posts/2026-07-25-a-local-model-arena-for-picking-the-right-model.md
new file mode 100644
index 0000000..b4a6034
--- /dev/null
+++ b/src/content/posts/2026-07-25-a-local-model-arena-for-picking-the-right-model.md
@@ -0,0 +1,47 @@
+---
+title: "A Local Model Arena for Picking the Right Model"
+description: "Vendor leaderboards are marketing until you test on your own work. So I built an arena that pits models head-to-head on my actual tasks and scores them on evidence."
+date: 2026-07-25
+tags: ["ai", "benchmarking", "local-models", "evaluation", "founder-log"]
+---
+
+Every week there's a new model claiming to beat something ten times its size. Sometimes it's true. Usually it's true *on the benchmark the vendor chose*. I got tired of guessing, so I built a local arena that answers the only question I actually care about: is this model better **on my work**?
+
+## The Problem
+
+Model leaderboards are real but misleading. A model tuned to win a public benchmark tells you how it does on that benchmark — not how it does on your specific tasks, with your prompts, at your latency and cost constraints. "9B beats 35B on coding" is a headline, not a decision. Adopting a model off a leaderboard is adopting a stranger's test suite as your own.
+
+## The Fix
+
+The arena is simple and stubbornly empirical:
+
+- **A fixed suite of my real tasks.** Not synthetic puzzles — the actual jobs I run: fixing a piece of code, extracting structured data from messy input, classifying and triaging items. The things a model would actually be doing for me.
+- **Head-to-head runs.** A candidate model and my current models all run the same suite, so the comparison is apples to apples.
+- **Scored on what matters: pass rate, latency, and cost.** A model that's slightly more accurate but three times slower might lose. One that's free to run locally has a standing advantage over one that bills per call. The score reflects the real tradeoff, not just quality in a vacuum.
+
+The output isn't a vibe ("this one feels smart"). It's a table that says which model won on my tasks and by how much — evidence I can act on.
+
+## The Lesson
+
+**A benchmark you didn't design is marketing; a benchmark built from your own work is a decision tool.** The only meaningful test of a model is how it performs on the specific things you'll ask it to do, weighed against what it costs you to run. Building that test once — a fixed suite of real tasks with honest scoring — turns every future "should I switch models?" from a gut call into a measurement. The next hyped model gets run through the arena, not adopted on faith.
+
+## Why This Matters for Business Owners
+
+The AI space moves fast, and the pressure to chase every new model is real. Don't. Build a small, honest evaluation from your own actual use cases and make every model prove itself against it. Most won't beat what you have; the few that do will show it in the numbers, on your tasks, including cost. That discipline saves you from expensive migrations chasing headlines — and it means when you *do* switch, you switch because the evidence said so, not because a leaderboard did.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
diff --git a/src/content/posts/2026-07-28-the-guardrail-that-refused-my-own-command.md b/src/content/posts/2026-07-28-the-guardrail-that-refused-my-own-command.md
new file mode 100644
index 0000000..75cc7e2
--- /dev/null
+++ b/src/content/posts/2026-07-28-the-guardrail-that-refused-my-own-command.md
@@ -0,0 +1,47 @@
+---
+title: "The Guardrail That Refused My Own Command"
+description: "I told my own automation to push some projects to private repos. It correctly refused an ambiguous version of the command first — and that refusal is exactly what I want."
+date: 2026-07-28
+tags: ["safety", "automation", "guardrails", "ai", "founder-log"]
+---
+
+Today I asked my own automation to do something irreversible — push a couple of projects up to private repositories — and the best part of the day was that it *said no first*. My initial instruction was ambiguous, a guardrail caught it, and it refused to act until I confirmed clearly. That refusal is the whole point.
+
+## The Setup
+
+Pushing code to a remote repository is one of those actions that's mostly fine and occasionally a disaster. Push the wrong thing, push something that should have stayed local, push to the wrong place — and depending on what's in it, you may not be able to fully take it back. It's exactly the kind of outward-facing, hard-to-reverse action that automation should treat with suspicion.
+
+I've built my systems so that these actions require an explicit, unambiguous go. Not a shrug. Not a maybe. A clear confirmation.
+
+## What Happened
+
+My first phrasing of the request was ambiguous enough that it *could* have meant something broader than I intended. Instead of guessing — instead of helpfully doing the expansive interpretation — the guardrail stopped and refused. It made me restate the request as an explicit confirmation before it would proceed.
+
+Only after I gave a clean, unambiguous "yes, this, exactly this" did it go: it created the private repositories and pushed. Two projects, done, and nothing pushed that I didn't mean to push.
+
+The sequence matters: **it blocked the ambiguous version, then executed the confirmed one.** That's not the automation being difficult. That's it being trustworthy.
+
+## The Lesson
+
+**You want your automation to be hardest to use for the actions that are hardest to undo.** Friction is usually the enemy of good tooling — except right in front of the irreversible stuff, where a moment of "are you sure you meant exactly this?" is worth everything. The failure mode to fear isn't automation that occasionally makes you confirm. It's automation that helpfully interprets an ambiguous command in the most expansive way and does something you can't take back. A system that refuses to guess on the dangerous actions is a system you can actually delegate to.
+
+## Why This Matters for Business Owners
+
+As you hand more to automation — and you should — the question that determines whether you sleep at night is: what does it do when it's *not sure* what you meant? Good automation treats ambiguity as a stop sign for anything irreversible, not as license to pick an interpretation and run. When you evaluate a tool or an AI workflow that can take real, outward-facing actions on your behalf, test the ambiguous command on purpose. If it guesses, don't trust it with the irreversible stuff. If it stops and asks, you've found something you can build on.
+
+## Let's Connect
+
+I build AI-powered automation for real businesses — not demos, not prototypes, production systems that run 24/7.
+
+If you're a **founder, entrepreneur, or small business owner** looking to automate operations with AI, let's talk:
+
+- [**@agentabrams on YouTube**](https://youtube.com/@AgentAbrams) — walkthroughs and demos
+- [**@agentabrams on X**](https://x.com/agentabrams) — DMs open
+- [**@agentabrams on Bluesky**](https://bsky.app/profile/agentabrams.bsky.social) — follow along
+- [**goodquestion.ai**](https://goodquestion.ai) — you're here
+
+**Advisory & Board Opportunities:** I'm actively looking to join boards where AI automation can drive real business value. If your company is exploring AI-driven operations, data pipelines, or autonomous agent systems — I'd love to contribute as a board member or advisor. Reach out on any platform above.
+
+---
+
+*Built with [Claude Code](https://claude.ai). Shipped in production. Every day. This is what one founder + AI looks like.*
← 715b360 security: full-genericize business secrets from public site,
·
back to Goodquestion Ai
·
(newest)