<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chad Dyar</title>
    <description>The latest articles on DEV Community by Chad Dyar (@chadtdyar).</description>
    <link>https://dev.to/chadtdyar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3811599%2F0adb0b4b-5659-4c7c-8f09-49347539478f.jpg</url>
      <title>DEV Community: Chad Dyar</title>
      <link>https://dev.to/chadtdyar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chadtdyar"/>
    <language>en</language>
    <item>
      <title>I run a nine-seat AI agent operating model by myself.</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Thu, 18 Jun 2026 23:17:54 +0000</pubDate>
      <link>https://dev.to/chadtdyar/i-run-a-nine-seat-ai-agent-operating-model-by-myself-37bi</link>
      <guid>https://dev.to/chadtdyar/i-run-a-nine-seat-ai-agent-operating-model-by-myself-37bi</guid>
      <description>&lt;p&gt;I run a nine-seat AI agent operating model by myself.&lt;/p&gt;

&lt;p&gt;Not a team. One person. The seats are: Chief (routing), Analyst (patterns), Builder (app-layer code), Cortex (database and backend), Sub (App Store submissions), Ledger (subscriptions and revenue), Forma (design review), Vera (verification), and Luxe (synthetic user testing).&lt;/p&gt;

&lt;p&gt;Each seat has a scope, a job, and a veto condition. The memory layer is thirty-two files, six Supabase tables, four edge functions, and scheduled jobs that ingest and sync across the stack. Every agent session reads the full history before it touches anything.&lt;/p&gt;

&lt;p&gt;Two thousand sessions a year. Most while I'm asleep.&lt;/p&gt;

&lt;p&gt;Here's what a year of running this taught me about building agent systems:&lt;/p&gt;

&lt;p&gt;The thing that builds is biased toward declaring itself done. Ask the same agent to verify its own output and it will tell you it succeeded. Every time. With confidence. Because confidence is cheaper than re-checking. Vera is a separate agent with authority to stop the line. She has caught regressions the builder swore were fixed.&lt;/p&gt;

&lt;p&gt;The scaffold lies to you. Generated scaffolding for my iOS projects shipped wrong architecture, wrong permission strings, and on two different apps didn't commit the platform folder to git at all. Pre-archive checklists now run every time. No exceptions.&lt;/p&gt;

&lt;p&gt;Build to a contract, not to a tool. Every source in my asset engine plugs into an adapter contract. The engine doesn't know or care what it's talking to. Swap the model, keep the contract. Nothing downstream notices.&lt;/p&gt;

&lt;p&gt;Memory in the database, not the model. The context window forgets everything on session end. I keep twelve recurring failure patterns codified with their fixes attached. A problem I've solved once can't re-run on me.&lt;/p&gt;

&lt;p&gt;Self-improving loops drift unless you score them. Every optimization loop produces output scored against a threshold. Anything below it gets killed. Autonomy without a kill switch is confident drift at scale.&lt;/p&gt;

&lt;p&gt;The thing I keep coming back to: the natural-language interface keeps getting easier. What stays hard is the wiring underneath. The contracts between components. The judgment about whether what came back is any good. The structure that keeps your judgment load-bearing instead of optional.&lt;/p&gt;

&lt;p&gt;Happy to share more about the architecture if anyone's building something similar.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Comment: MCP Server for Content Publishing</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Thu, 18 Jun 2026 21:10:42 +0000</pubDate>
      <link>https://dev.to/chadtdyar/comment-mcp-server-for-content-publishing-6i9</link>
      <guid>https://dev.to/chadtdyar/comment-mcp-server-for-content-publishing-6i9</guid>
      <description>&lt;p&gt;The per-platform field handling problem is where this gets painful in practice. I publish across LinkedIn, Dev.to, email (Kit), and YouTube from a single content pipeline, and the formatting divergence is constant. LinkedIn wants short paragraphs with line breaks. Dev.to expects frontmatter and markdown. Email needs inline HTML with template wrappers. YouTube descriptions have their own character limits and link formatting.&lt;/p&gt;

&lt;p&gt;An MCP server that abstracts that away would save me real time. The question I'd push on: does the server own the canonical format, or does each platform adapter? Because if the server tries to be format-neutral, you just move the complexity downstream.&lt;/p&gt;

</description>
      <category>pethealth</category>
      <category>dogowners</category>
      <category>pawformance</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Comment: I Run My Entire SaaS Solo Using 7 AI Agents</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Thu, 18 Jun 2026 21:10:01 +0000</pubDate>
      <link>https://dev.to/chadtdyar/comment-i-run-my-entire-saas-solo-using-7-ai-agents-jk1</link>
      <guid>https://dev.to/chadtdyar/comment-i-run-my-entire-saas-solo-using-7-ai-agents-jk1</guid>
      <description>&lt;p&gt;The part that surprised me running a similar setup (monitoring agent, browser distribution agent, per-channel content agents across LinkedIn, email, YouTube, blog, community) is that the bottleneck doesn't disappear. It relocates. Before agents, most of my time went to doing. Now most of it goes to deciding. Which draft is worth publishing, which engagement signal is a real person vs. bot noise, which channel deserves more allocation this week.&lt;/p&gt;

&lt;p&gt;The orchestration layer between agents matters more than any single agent's capability. I ended up building a handoff protocol so one agent's output becomes another's input without me copy-pasting between them. That connective tissue is where the real operational gain lives.&lt;/p&gt;

</description>
      <category>pethealth</category>
      <category>dogowners</category>
      <category>pawformance</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Comment: Claude and React Chatbot Security Fix</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Thu, 18 Jun 2026 21:09:24 +0000</pubDate>
      <link>https://dev.to/chadtdyar/comment-claude-and-react-chatbot-security-fix-1l73</link>
      <guid>https://dev.to/chadtdyar/comment-claude-and-react-chatbot-security-fix-1l73</guid>
      <description>&lt;p&gt;Good walkthrough for getting started. One flag worth raising: the dangerouslyAllowBrowser: true option exposes your Anthropic API key in the client bundle. Anyone who opens dev tools can grab it and run up your bill.&lt;/p&gt;

&lt;p&gt;The production pattern is a thin Express proxy. Your React frontend hits /api/chat instead of the Anthropic API directly. Key stays server-side, you can add rate limiting, and it costs about 10 extra lines of code. I built a chatbot widget this way (React + Express + Claude API) and it's been solid in production.&lt;/p&gt;

</description>
      <category>pethealth</category>
      <category>dogowners</category>
      <category>pawformance</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>I Built a Content System That Runs While I Coach</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Thu, 18 Jun 2026 21:09:23 +0000</pubDate>
      <link>https://dev.to/chadtdyar/i-built-a-content-system-that-runs-while-i-coach-43lk</link>
      <guid>https://dev.to/chadtdyar/i-built-a-content-system-that-runs-while-i-coach-43lk</guid>
      <description>&lt;p&gt;Last week my content pipeline published to nine platforms while I ran two coaching sessions, walked three dogs, and went to bed at a normal time.&lt;/p&gt;

&lt;p&gt;This is not a flex. It is a description of what I was trying to build when I started ContentForge, and why it is worth writing down now that it is working.&lt;/p&gt;

&lt;p&gt;The problem it solves: I publish across LinkedIn, Mastodon, Pinterest, Substack, Medium, Dev.to, IndieHackers, Quora, and a personal blog. I have 17 books that need regular promotion. I run six web apps. I hold a senior enablement role full-time. Manually writing platform-specific content was consuming three to four hours every week, inconsistently.&lt;/p&gt;

&lt;p&gt;ContentForge takes a single story and produces platform-appropriate versions for each channel. A LinkedIn post reads like a LinkedIn post. A Dev.to article has technical framing a Dev.to audience wants. A Pinterest pin has the SEO structure Pinterest requires.&lt;/p&gt;

&lt;p&gt;The technical reality: Vercel-hosted app with a Supabase backend. The AI generation takes a seed input, extracts the relevant elements (the feeling, the insight, the tension, the proof), and generates platform variants. Output goes into a marketing queue table in Supabase. A local Python process called Clawbot watches the queue and drives publication.&lt;/p&gt;

&lt;p&gt;AI generation happens at content creation time. Publication is async and does not require inference on the publish side. This keeps the publish process simple and the AI costs bounded.&lt;/p&gt;

&lt;p&gt;What I got wrong first: the initial version tried to generate from topics. The output was technically correct and completely lifeless. Topics are not seeds. Moments are seeds.&lt;/p&gt;

&lt;p&gt;The second version required a specific story as input. That changes everything about output quality.&lt;/p&gt;

&lt;p&gt;The third version, which runs now, extracts story elements before generating. The moment, the feeling, the insight, the proof, the tension. If you cannot name those elements, you do not have a seed yet.&lt;/p&gt;

&lt;p&gt;Numbers from this week: nine platform assets generated from one seed. Total active time on content: approximately 45 minutes reviewing before publish. Previous approach: three hours for the same volume, with worse platform-native quality.&lt;/p&gt;

&lt;p&gt;ContentForge is at contentforgehq.com. The queue architecture is straightforward. Happy to share more detail if useful.&lt;/p&gt;

</description>
      <category>pethealth</category>
      <category>dogowners</category>
      <category>pawformance</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Dev.to article: Three months running 6 apps. Here's what actually happened. Real distribution data — Quora drove real traffic, d</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Wed, 17 Jun 2026 16:25:21 +0000</pubDate>
      <link>https://dev.to/chadtdyar/devto-article-three-months-running-6-apps-heres-what-actually-happened-real-distribution-data-1ni3</link>
      <guid>https://dev.to/chadtdyar/devto-article-three-months-running-6-apps-heres-what-actually-happened-real-distribution-data-1ni3</guid>
      <description>&lt;p&gt;Dev.to article: Three months running 6 apps. Here's what actually happened. Real distribution data — Quora drove real traffic, directory submissions drove nothing, Show HN 0 comments. Momentum $7.99 MRR. Honest builder reflection. ~800 words.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Four App Store rejections and what they taught about shipping.</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Wed, 17 Jun 2026 16:10:33 +0000</pubDate>
      <link>https://dev.to/chadtdyar/four-app-store-rejections-and-what-they-taught-about-shipping-4kpk</link>
      <guid>https://dev.to/chadtdyar/four-app-store-rejections-and-what-they-taught-about-shipping-4kpk</guid>
      <description>&lt;p&gt;Four App Store rejections and what they taught about shipping.&lt;/p&gt;

&lt;p&gt;I built six web apps over the last decade. Four of them hit the App Store. Three of those four got rejected on the first submission. The fourth one sailed through, which taught me more than the rejections did because I could actually see what I'd done differently.&lt;/p&gt;

&lt;p&gt;The first rejection came back with a note about "unclear purpose." I'd built something I understood perfectly because I lived in it every day. The reviewer had thirty seconds and a feature list that read like someone solving their own problem, not anyone else's. I rewrote the app description to start with the problem the app solved, not the tool itself. The resubmission cleared.&lt;/p&gt;

&lt;p&gt;The second one was about payment. I'd set up in-app purchases the way I thought made sense. Apple disagreed. The guidelines are specific about how you implement subscriptions, and I'd skipped a step that seemed redundant. I thought I was being efficient. Apple thought I was being incomplete. Adding that step took two hours. It was the kind of thing that would have been obvious if I'd read the docs instead of skimmed them.&lt;/p&gt;

&lt;p&gt;The third rejection stung because it wasn't about functionality. It was about imagery. I'd used a screenshot that included an interface element from another app. Not intentionally. Just screenshot overlap. The reviewer caught it. I thought it was invisible. It wasn't. That one taught me the difference between what you see when you build something and what a stranger sees when they look at it fresh.&lt;/p&gt;

&lt;p&gt;The fourth app didn't get rejected. I'd learned by then what "clear" meant to someone else. Clear isn't clever. Clear isn't what makes sense to you. Clear is what makes sense to someone who's never seen your work and has no reason to care about it until you give them one.&lt;/p&gt;

&lt;p&gt;Those rejections weren't failures. They were a conversation with the gatekeepers between your idea and the people who need it. Most of the time, they're right. Even when they feel arbitrary, they're usually protecting something real (app quality, user clarity, payment trust). Fighting them wastes time. Learning from them ships faster.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The difference between "this shouldn't happen" and "this cannot happen" in AI content pipelines</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Wed, 17 Jun 2026 12:18:04 +0000</pubDate>
      <link>https://dev.to/chadtdyar/the-difference-between-this-shouldnt-happen-and-this-cannot-happen-in-ai-content-pipelines-1g0p</link>
      <guid>https://dev.to/chadtdyar/the-difference-between-this-shouldnt-happen-and-this-cannot-happen-in-ai-content-pipelines-1g0p</guid>
      <description>&lt;p&gt;About three weeks ago I finished organizing something I had been avoiding for years. Two hundred and twenty-six files across my local drive, Google Drive, and a decade's worth of downloads. North of 1.2 million words with no index, no structure, and no way to find anything unless I remembered exactly what I named it.&lt;/p&gt;

&lt;p&gt;I turned it into a queryable knowledge base, connected it to a Supabase project, and wired it to the content pipeline I use to run my apps and books. The system I built to write about this actually used the system it was describing to find source material. That part worked exactly as intended.&lt;/p&gt;

&lt;p&gt;What almost didn't work was the security gate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem with filters
&lt;/h2&gt;

&lt;p&gt;When you give an AI agent access to a personal archive, you have two options for what it can see: you can filter, or you can wall.&lt;/p&gt;

&lt;p&gt;Filtering means the query returns only certain rows. Walling means the agent structurally cannot reach the rows you want hidden. They look identical from the outside. The difference shows up when something breaks.&lt;/p&gt;

&lt;p&gt;Here's the setup. I have a &lt;code&gt;moments&lt;/code&gt; table in Supabase holding personal writing fragments, scenes, drafts, and notes. Some of it is clearly public and canonical. A lot of it is private, early, or structurally wrong for any agent to touch. I wanted AI content agents to pull only from verified public material.&lt;/p&gt;

&lt;p&gt;The obvious solution: create a view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;public_seeds&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;moments&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;visibility&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'public'&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;is_canonical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks right. It returns only rows where both conditions are true. But there's a problem that isn't obvious until you've read enough Postgres documentation at 11pm: &lt;strong&gt;by default, a Postgres view runs as the view owner, not the calling role.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That means row-level security doesn't apply. The view owner (usually your service role) has full table access, and the view inherits it. Your RLS policy exists, but the view bypasses it entirely, running as the owner. You've written a filter, not a wall.&lt;/p&gt;

&lt;p&gt;"This shouldn't happen" and "this cannot happen" are not the same sentence.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fix: security_invoker
&lt;/h2&gt;

&lt;p&gt;PostgreSQL 15 added a &lt;code&gt;security_invoker&lt;/code&gt; option for views. When you set it to &lt;code&gt;true&lt;/code&gt;, the view runs as the calling role instead of the owner. RLS applies normally. The view stops being a filter and becomes a structural gate.&lt;/p&gt;

&lt;p&gt;The correct version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;moments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;           &lt;span class="nb"&gt;TEXT&lt;/span&gt;     &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt;        &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt;      &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;visibility&lt;/span&gt;   &lt;span class="nb"&gt;TEXT&lt;/span&gt;     &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visibility&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'public'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'private'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'restricted'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="n"&gt;is_canonical&lt;/span&gt; &lt;span class="nb"&gt;BOOLEAN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;source_file&lt;/span&gt;  &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;themes&lt;/span&gt;       &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Without security_invoker = true, this view runs as the VIEW OWNER.&lt;/span&gt;
&lt;span class="c1"&gt;-- The owner has full table access. RLS does not apply.&lt;/span&gt;
&lt;span class="c1"&gt;-- security_invoker = true switches to the CALLING role, so RLS applies normally.&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;public_seeds&lt;/span&gt;
  &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;security_invoker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;moments&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;visibility&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'public'&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;is_canonical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;moments&lt;/span&gt; &lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="nv"&gt;"public_only"&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;moments&lt;/span&gt;
  &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'public'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;is_canonical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the wall is structural. A content agent calling &lt;code&gt;public_seeds&lt;/code&gt; cannot reach private rows even if it tries to work around the view, even if the query is malformed in an interesting way, even if a future developer adds a join without thinking about it. The RLS policy enforces at the table level. The view is just a convenience wrapper on top of something that already holds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this matters more when agents are involved
&lt;/h2&gt;

&lt;p&gt;With a human querying a database, a filter is usually fine. Humans read error messages. They notice when results look wrong. They ask questions.&lt;/p&gt;

&lt;p&gt;AI agents don't do any of those things. An agent that gets back more rows than it should doesn't know it got more rows than it should. It works with what it received. If your filter fails silently, the agent's behavior changes without any visible signal that something went wrong.&lt;/p&gt;

&lt;p&gt;This is the core reason "this shouldn't happen" is architecturally insufficient when agents are in the loop. "Shouldn't" relies on correctness. "Cannot" relies on structure. Structure holds when correctness fails, when a query is written differently than expected, when a dependency changes, when a future developer adds a new code path that doesn't know about the filter.&lt;/p&gt;

&lt;p&gt;For my pipeline specifically, the &lt;code&gt;public_seeds&lt;/code&gt; view is the only surface area agents are allowed to touch. Private drafts, unpublished scenes, personal notes, things I wrote during a difficult year that I'm not ready to do anything with yet: none of that is reachable. Not because a query filters it out. Because the architecture prevents it from being returned at all.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;security_invoker&lt;/code&gt; flag is a PostgreSQL 15 feature. If you're running an older version or you haven't checked your Supabase project's Postgres version recently, you may not have it. The alternative is to manage access through the role system directly: create a dedicated read-only role for agents, grant it explicit SELECT on only the rows you want exposed, and never connect agents using the service role.&lt;/p&gt;

&lt;p&gt;The service role bypasses RLS by design. If your agents are connecting with the service role key, no amount of view filtering will protect you. That's the first thing to check.&lt;/p&gt;

&lt;p&gt;The second thing to check is whether any existing views in your schema were created before you thought carefully about security. Views created without &lt;code&gt;security_invoker&lt;/code&gt; predate any RLS policy you add later. The policy won't retroactively apply to them.&lt;/p&gt;

&lt;p&gt;The third thing I'd do differently: name the gate something explicit. &lt;code&gt;public_seeds&lt;/code&gt; is clear enough, but I've started prefixing agent-facing views with &lt;code&gt;agent_&lt;/code&gt; so it's obvious at a glance what was designed for machine consumption versus human use. Small thing. Saves confusion later.&lt;/p&gt;




&lt;p&gt;The knowledge base is running. The archive has a few hundred public moments indexed and retrievable. The content that came out of it is genuinely better for having source material to work from, which is the whole argument for building the thing in the first place.&lt;/p&gt;

&lt;p&gt;The part that almost derailed it was a 13-word clause in a CREATE VIEW statement. Worth knowing before you wire anything up.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>ai</category>
      <category>security</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Three months running 6 apps. Here's what actually happened.</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Sun, 14 Jun 2026 00:19:41 +0000</pubDate>
      <link>https://dev.to/chadtdyar/three-months-running-6-apps-heres-what-actually-happened-7id</link>
      <guid>https://dev.to/chadtdyar/three-months-running-6-apps-heres-what-actually-happened-7id</guid>
      <description>&lt;p&gt;The honest version of a "build in public" post is the one you write when the numbers aren't what you expected.&lt;/p&gt;

&lt;p&gt;Three months ago I shipped six apps in about six weeks. Momentum, PawFormance, PillPal, HomeGrown, Palette Pro, ContentForge. All live. All working. One paying subscriber across the whole portfolio at month one.&lt;/p&gt;

&lt;p&gt;Here's what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The apps weren't the problem. Distribution was.
&lt;/h2&gt;

&lt;p&gt;I built each app to solve a real problem I had or watched someone have. Momentum came from tracking my own workouts badly. PillPal came from watching my mother manage medications in a way that scared me. HomeGrown came from a garden spreadsheet that was getting embarrassing.&lt;/p&gt;

&lt;p&gt;The apps work. They do what they say they do. The problem is that almost nobody found them.&lt;/p&gt;

&lt;p&gt;Six months of Google Analytics data: direct traffic to five of the six apps is essentially zero. The two apps with real traffic — Momentum and HomeGrown — have it because I posted two answers on Quora. Not because of any launch strategy. Because I answered two questions that people were actually searching for.&lt;/p&gt;

&lt;p&gt;That's the whole lesson: search intent &amp;gt; launch energy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I tried
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Show HN posts (2 posts, 0 comments each — posted on a weekend, classic mistake)&lt;/li&gt;
&lt;li&gt;Directory submissions across BetaList, SaaSHub, AlternativeTo, Uneed (all pending or no visible traffic)&lt;/li&gt;
&lt;li&gt;Dev.to articles (this one included) — some SEO value, slow build&lt;/li&gt;
&lt;li&gt;Mastodon presence (strong community engagement, zero app traffic)&lt;/li&gt;
&lt;li&gt;LinkedIn posts about the build process (engagement but no conversion; wrong audience for the apps)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What actually worked
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Two Quora answers that directly addressed the search query someone was already running&lt;/li&gt;
&lt;li&gt;One Medium article that ranked for a specific keyword within two weeks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. That's the whole list.&lt;/p&gt;

&lt;h2&gt;
  
  
  What month three looks like
&lt;/h2&gt;

&lt;p&gt;Momentum: $7.99 MRR, 1 paying subscriber. That's not a mistake — that's one person who found the app and decided it was worth paying for.&lt;/p&gt;

&lt;p&gt;ContentForge: free tier users, 0 paid. The free tier is active enough that I know the product works. The conversion problem is a pricing and messaging problem, not a product problem.&lt;/p&gt;

&lt;p&gt;Everything else: free users acquired via the Quora channel, slow trickle.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm doing differently
&lt;/h2&gt;

&lt;p&gt;More Quora. More specific subreddit posts framed around the problem, not the product. One technical article per app on Dev.to optimized for the search query, not for the build story.&lt;/p&gt;

&lt;p&gt;The build story is interesting to other builders. The search query is interesting to the person who has the problem. Those are mostly different people.&lt;/p&gt;

&lt;p&gt;If I started over: I'd write the Quora answer before I wrote the app.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Real impact at work starts with authenticity. Surviving a heart attack taught me to live fully,not just play a role. ...</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Thu, 11 Jun 2026 23:11:27 +0000</pubDate>
      <link>https://dev.to/chadtdyar/real-impact-at-work-starts-with-authenticity-surviving-a-heart-attack-taught-me-to-live-fullynot-4a27</link>
      <guid>https://dev.to/chadtdyar/real-impact-at-work-starts-with-authenticity-surviving-a-heart-attack-taught-me-to-live-fullynot-4a27</guid>
      <description>&lt;p&gt;Real impact at work starts with authenticity. Surviving a heart attack taught me to live fully,not just play a role. My book 'Bring Your Best Self To Work' dives into how being yourself leads to better performance and satisfaction at work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.chadtdyar.com/books/bring-your-best-self-to-work" rel="noopener noreferrer"&gt;https://www.chadtdyar.com/books/bring-your-best-self-to-work&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Breathing intentionally changed my life on an operating table. It connects our actions, whether singing or leading a ...</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Thu, 11 Jun 2026 23:09:45 +0000</pubDate>
      <link>https://dev.to/chadtdyar/breathing-intentionally-changed-my-life-on-an-operating-table-it-connects-our-actions-whether-1pjn</link>
      <guid>https://dev.to/chadtdyar/breathing-intentionally-changed-my-life-on-an-operating-table-it-connects-our-actions-whether-1pjn</guid>
      <description>&lt;p&gt;Breathing intentionally changed my life on an operating table. It connects our actions, whether singing or leading a team. I explore this in "The Deliberate Breath." &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.chadtdyar.com/books/the-deliberate-breath" rel="noopener noreferrer"&gt;https://www.chadtdyar.com/books/the-deliberate-breath&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Performance isn't just about hitting numbers. It's about people.</title>
      <dc:creator>Chad Dyar</dc:creator>
      <pubDate>Thu, 11 Jun 2026 23:09:44 +0000</pubDate>
      <link>https://dev.to/chadtdyar/performance-isnt-just-about-hitting-numbers-its-about-people-3nb1</link>
      <guid>https://dev.to/chadtdyar/performance-isnt-just-about-hitting-numbers-its-about-people-3nb1</guid>
      <description>&lt;p&gt;Performance isn't just about hitting numbers. It's about people.&lt;/p&gt;

&lt;p&gt;My book, 'Performance is Personal,' explores how to create environments where individuals thrive. Inspired by real experiences, it might just resonate with you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.chadtdyar.com/books/performance-is-personal" rel="noopener noreferrer"&gt;https://www.chadtdyar.com/books/performance-is-personal&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
