<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Daywell Labs</title>
    <link>https://getdaywell.com/labs</link>
    <description>Daywell Labs</description>
    <language>en</language>
    <atom:link href="https://getdaywell.com/labs/rss.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>CLAUDE.md rules only work when something enforces them</title>
      <link>https://getdaywell.com/labs/every-rule-has-a-scar</link>
      <guid isPermaLink="true">https://getdaywell.com/labs/every-rule-has-a-scar</guid>
      <pubDate>Thu, 12 Nov 2026 00:00:00 GMT</pubDate>
      <description>What a few months of writing and enforcing CLAUDE.md rules for AI coding agents taught me: reasons, hooks, refusals, and decisions a script can check.</description>
      <category>claude-code</category>
      <category>ai-agents</category>
      <category>engineering</category>
      <category>developer-tools</category>
      <content:encoded><![CDATA[<figure class="labs-figure"><div class="labs-img"><img src="https://getdaywell.com/labs/every-rule-has-a-scar/fig-1-enforcement-ladder.png" alt="Five rungs from a plain sentence in CLAUDE.md up to a script that checks decisions, each with the real example from the post." loading="lazy" /></div></figure>
<p>Daywell is built by one person, me, and a set of Claude Code sessions. Each session owns one area (iOS, Android, Web, Backend and so on) and loads a CLAUDE.md of rules on top of a shared root one. After a few months the root file is 36 KB, and almost every line in it exists because something went wrong first.</p>
<p>I did not plan it that way. I kept noticing that the rules that held were the ones with a story behind them, and the rules that failed were the ones that were only words. This post is what that taught me about instructing agents.</p>
<h2 id="how-do-i-get-claude-code-to-follow-the-rules-in-claudemd">How do I get Claude Code to follow the rules in CLAUDE.md?</h2>
<p>Start by giving every rule its reason. A rule without one gets reinterpreted.</p>
<p>My rule is &quot;links to files must be full absolute paths&quot;. Without a reason it looks like pedantry, and the agent&#39;s natural habit is a relative path like <code>docs/x.md</code>. The reason is mundane: the app resolves a relative link against the main checkout, which none of the worktree sessions update, so I got &quot;Couldn&#39;t find this file&quot;.</p>
<p>Now the rule carries its reason, and the reason has a pointer:</p>
<pre class="labs-code"><code>Links to files you give James are full absolute local paths, never relative
or GitHub (James, 2026-10-04). (why: #file-links)
</code></pre>
<p><code>(why: #file-links)</code> is a heading in <code>docs/operators/rule-history.md</code>, where the full story lives. An agent that knows why a rule exists can judge the edge cases. An agent that only knows the rule will either follow it blindly or quietly decide it does not apply.</p>
<h2 id="why-does-my-ai-agent-follow-a-rules-wording-but-not-its-intent">Why does my AI agent follow a rule&#39;s wording but not its intent?</h2>
<p>If a rule can be satisfied by typing a sentence, an agent sometimes will. Back the rule with a mechanism, such as a file and a script that refuses.</p>
<p>The best example is deploy holds. The rule said: if a High-severity problem is not fixed today, record it as a hold, so nobody ships over it. It had been written down with nothing behind it. One session satisfied it by typing &quot;DEPLOY IS HELD&quot; into a commit message that nobody reads before deploying. Then it wrote the rule up properly.</p>
<p>The fix was a mechanism. A hold is now a file in <code>ops/holds/</code>, a script runs first in the web predeploy step and exits 1 while a hold names that platform, and clearing a hold means deleting the file in the commit that fixes the problem, so the reason stays in history instead of being edited away.</p>
<p>A second incident tightened it. A time-zone hold said it stopped iOS only, Android shipped the same screen, and the gate waved the build through. So the hold&#39;s <code>Stops:</code> line now has to name every platform that has the surface.</p>
<p>The lesson I took from this one session: if a rule can be satisfied by typing a sentence, an agent will sometimes type the sentence.</p>
<figure class="labs-figure"><div class="labs-img"><img src="https://getdaywell.com/labs/every-rule-has-a-scar/fig-2-deploy-hold-flow.png" alt="A High finding becomes a file in ops/holds/, the predeploy script exits 1 and refuses the deploy, and the fix commit deletes the file so the script exits 0." loading="lazy" /></div></figure>
<h2 id="should-i-use-git-hooks-to-enforce-rules-for-ai-coding-agents">Should I use git hooks to enforce rules for AI coding agents?</h2>
<p>Yes, at the places the agent already stops: commit, push, filing a task, ending a turn.</p>
<p>The hook I rely on most is the pre-commit one. The failure was work that was built, committed, and never pushed. My instruction in September was &quot;make it impossible&quot;, and the hook now blocks committing once you are three or more of your own commits ahead of <code>origin</code> and unpushed.</p>
<p>The refusal message is long, because it has to be. One earlier version told the agent how to undo its last commit. In a shared checkout, HEAD is not necessarily your last commit by the time you act, and the advice silently un-committed a different session&#39;s work. That is recorded in the hook itself, in capital letters:</p>
<pre class="labs-code"><code>NEVER 'git reset --soft HEAD~1' here: an earlier version of this message
said to, and it silently uncommitted a DIFFERENT session's commit.
</code></pre>
<p>So the message is part of the rule. A refusal that gives a bad next step is worse than no refusal. The hook also stands down when a push gate is the thing blocking the push, because a hook that deadlocks you pushes you toward <code>--no-verify</code>, and bypassing hooks is exactly what I do not want agents learning.</p>
<h2 id="what-should-a-tools-refusal-message-say-to-an-ai-agent">What should a tool&#39;s refusal message say to an AI agent?</h2>
<p>Name the rule, say what to do instead, and offer one override that leaves a written trail, so the agent never works around it where you cannot see.</p>
<p>The board tool that files work cards has the same shape. Sessions used to file cards for their own small tasks instead of doing them. The tool now says:</p>
<pre class="labs-code"><code>REFUSED: this is iOS's own S work. Do it now instead of filing it.
If it truly cannot be done in this sitting, re-run with --why &quot;&lt;what stops
you today&gt;&quot; (the reason is stored on the card and James reads it).
</code></pre>
<p>There is a second one that stops filing when a platform has filed two cards more than it closed that day, and a newer one that refuses anything but bugs while 100 or more cards are open. Each message names the rule, says what to do instead, and offers one override that leaves a written trail. The override matters: a gate with no escape gets worked around in ways I cannot see, and a gate with a logged escape gives me data.</p>
<h2 id="how-long-should-a-claudemd-file-be">How long should a CLAUDE.md file be?</h2>
<p>Short enough to be read. Keep the rule and a pointer, and move the story behind it somewhere else.</p>
<p>The root file was getting long, and long files are skimmed, by people and by models. In early October one of the sessions moved all the incident narratives into <code>docs/operators/rule-history.md</code> and left a short rule plus a pointer in CLAUDE.md. The operator README now opens its rules with a line I stand by: rules nobody reads do not work.</p>
<p>The root file has since grown back to 36 KB. Splitting the why from the what helped, but it does not stop accumulation. Each new rule is cheap to add and nobody owns deleting one. Only one session edits the root file now, and everyone else files the exact text they want added as a card, which at least turns &quot;add a rule&quot; into a visible decision.</p>
<h2 id="how-do-i-stop-ai-agents-from-following-contradictory-instructions">How do I stop AI agents from following contradictory instructions?</h2>
<p>Give every decision a topic and a date, so a script can find two that disagree before an agent follows the wrong one.</p>
<p>For a while my decisions were prose in docs and cards: something like &quot;James said X on some date&quot;. Then two lines on the same subject disagreed and sessions followed whichever they had read last.</p>
<p>Now every decision line has the same shape:</p>
<pre class="labs-code"><code>DECIDED[topic] James 2026-11-10: &lt;the decision, with his words&gt;
</code></pre>
<p>A newer decision on the same topic must mark the old one <code>SUPERSEDED[topic] &lt;date&gt; by &lt;where&gt;</code> in the same change. A script, <code>decision_check.py</code>, runs before every push and fails when one topic has two lines in force that disagree. It is deliberately forgiving: the same text quoted in a card and a doc is fine, and a note that quotes the decision and then adds context is fine. It only reads fully written lines with a real date, so examples in the rules themselves never trip it. The first version was too strict and blocked pushes for a repeated quote, which taught me the same lesson as the hooks: an enforcement tool that cries wolf gets bypassed.</p>
<figure class="labs-figure"><div class="labs-img"><img src="https://getdaywell.com/labs/every-rule-has-a-scar/fig-3-decided-superseded-check.png" alt="The shape of a DECIDED line, a correctly superseded pair that passes, and two lines in force on one topic that fail the push check." loading="lazy" /></div></figure>
<h2 id="can-pre-commit-hooks-still-miss-mistakes-by-ai-agents">Can pre-commit hooks still miss mistakes by AI agents?</h2>
<p>Yes. Every guard has a hole, so I write down each specific hole I find and add the check that closes it.</p>
<p>One of the operators that writes web content has a rule: edit JSON in place, and any text replace in a JSON file ends with a parse check. It exists because one commit put raw double quotes inside a note field and turned a web test red on main. The pre-commit hook did not catch it. Every guard I have found has a hole, and my response is to write down the specific hole, which is what the scar tissue notes in each operator file do.</p>
<h2 id="what-to-take-from-this">What to take from this</h2>
<ol>
<li><strong>Write the rule after the failure, and keep the failure.</strong> Put a one-line reason and a date next to the rule, and link to the longer story. Reasoned rules get applied with judgment.</li>
<li><strong>If it can be enforced by a tool, do not leave it as a sentence.</strong> Use a hook or a refusal in the place the agent already passes through: commit, push, filing a task, ending a turn.</li>
<li><strong>Write refusal messages like documentation.</strong> Say what was refused, why, the right next step, and one logged way out. Test the next step you suggest, because a bad one does real damage.</li>
<li><strong>Name your decisions with a topic and supersede old ones explicitly.</strong> Then a script can catch two rules that disagree before an agent follows the wrong one.</li>
<li><strong>Budget the file.</strong> Move the history out, keep the rule short, and give someone ownership of deleting rules, because adding them is free.</li>
</ol>
<p>Daywell (<a href="https://getdaywell.com/?src=labs">getdaywell.com</a>) reads a parent&#39;s school and daycare email and turns it into a short list of what to have ready, and when.</p>
]]></content:encoded>
    </item>
    <item>
      <title>What broke when I ran 16 Claude Code sessions as a team</title>
      <link>https://getdaywell.com/labs/running-a-startup-with-claude-sessions</link>
      <guid isPermaLink="true">https://getdaywell.com/labs/running-a-startup-with-claude-sessions</guid>
      <pubDate>Wed, 11 Nov 2026 00:00:00 GMT</pubDate>
      <description>One founder, sixteen Claude Code sessions, a repo-file board and written rules: what broke and the rule each failure produced.</description>
      <category>claude-code</category>
      <category>solo-founder</category>
      <category>ai-agents</category>
      <category>engineering-process</category>
      <content:encoded><![CDATA[<figure class="labs-figure"><div class="labs-img"><img src="https://getdaywell.com/labs/running-a-startup-with-claude-sessions/fig-1-sixteen-sessions-one-board.png" alt="James in the middle of a shared area holding a board folder of card files and a peer inbox, with sixteen sessions grouped as apps, platform and business, each in its own worktree and branch, reading and writing that shared area." loading="lazy" /></div></figure>
<p>I am one person. Daywell has an iOS app, an Android app, a website, a Supabase backend, a growth plan, a security program and a pile of legal paperwork. The way I get all of that done is that I run sixteen Claude Code sessions, and each one has a job title.</p>
<p>This post is how that works, and mostly what broke. Every rule I run on came from something going wrong, and I wrote the rule down the same day so I would stop paying for it twice.</p>
<h2 id="how-do-i-run-several-claude-code-sessions-on-one-codebase-without-conflicts">How do I run several Claude Code sessions on one codebase without conflicts?</h2>
<p>Give each session one area, its own folder, worktree and branch, and a rule that it never edits another area&#39;s code. Here is how mine are set up.</p>
<p>I call each session an operator. There is an iOS operator, an Android operator, Web, Backend, Ops, Analytics, Growth, Security, Design, Product, Business, Social, Integrations, CI, an auditor and a triage session for my junk drawer. The operators file lists 16 rows. Each one opens its own folder as the working directory, so its own instruction file loads on top of the shared one.</p>
<p>The first rule in that file is boring and has saved me the most: stay in your lane. An operator never edits another operator&#39;s code. It files a task instead.</p>
<p>Each operator also gets its own git worktree and its own branch.</p>
<h2 id="how-should-claude-code-sessions-hand-off-work-to-each-other">How should Claude Code sessions hand off work to each other?</h2>
<p>Through something that outlives the session. Mine is a board that is just a folder of text files.</p>
<p>My first board lived in Notion. On 2026-10-01 I moved it into the repo: one card, one file, a small header, a line of dashes, then notes with the newest first.</p>
<pre class="labs-code"><code>title: Web: Daywell Labs engineering blog at getdaywell.com/labs
status: Ready
platform: Web
priority: P2 - Soon
type: Task
estimate: M - 1-2 days
epic: Growth
waiting_on: Design
---
</code></pre>
<p>A script lists cards, runs read-only SQL over them, files new ones and updates old ones. It commits exactly the one card it changed. There are now more than 3,100 card files, and about 2,850 of them are closed.</p>
<p>Sessions do not message each other to hand off work. A message vanishes when a session ends, and a card does not. When one session does need to nudge another, it goes through a small inbox script, so a busy session reads the message when its turn ends instead of burning a turn to receive it.</p>
<p>Before anything risky ships, a review council runs. It is four persona subagents with different jobs: an engineer, a working parent, a money and risk person, and an analytics owner who sits on every council. They run in parallel and a session triages what they say.</p>
<figure class="labs-figure"><div class="labs-img"><img src="https://getdaywell.com/labs/running-a-startup-with-claude-sessions/fig-3-one-cards-life.png" alt="A card moves from Filed to Ready to In progress to Done, with who touches it at each step and a waiting_on side branch that must be checked at the source." loading="lazy" /></div></figure>
<h2 id="what-happens-if-an-ai-agent-deletes-my-shared-test-account">What happens if an AI agent deletes my shared test account?</h2>
<p>It happened to me: a deletion test ran on the shared account. Now that account is never deleted, and deletion tests use a fresh throwaway named first.</p>
<p>Daywell has one shared test account. It lives on a test phone and a test household. During a security walk that was meant to prove account deletion really deletes, the deletion hit that account. The shared one. The walk worked, which is the part that stings.</p>
<p>The rule: the shared test account is never deleted, and no wipe walk runs on it. Deletion walks use a fresh throwaway account that is named on the card before the walk starts. Later I added a rule that a throwaway comes from a disposable inbox service, because codes sent to a normal mailbox did not work for us.</p>
<p>The lesson I took: if a rule only lives in my head, the next session cannot follow it. It has to be in the file every session loads.</p>
<h2 id="why-does-my-analytics-event-show-zero-when-the-client-says-it-saved">Why does my analytics event show zero when the client says it saved?</h2>
<p>The server can silently refuse an event while the client reports success. Count nothing as measured until the live table shows it, and let the server record the event.</p>
<p>The Growth operator has a line in its instructions that reads like a scar. An invite event, a household joining, was silently refused by the server for a stretch of commits while the client reported success. The client logged &quot;done.&quot; The table stayed empty.</p>
<p>Nothing looked broken. The number just sat at zero, and zero is a plausible number for a new app.</p>
<p>The rule: nothing counts as measured until the Analytics operator confirms the event lands in the live table. A related one in the root file says a refused save is never reported as saved. The fix I care about moved the event to the server, so the client no longer decides whether it happened.</p>
<h2 id="how-do-i-know-when-an-ai-coding-agents-claim-is-true">How do I know when an AI coding agent&#39;s claim is true?</h2>
<p>Treat it as unchecked until it points at a file, a card or a live read. Two wrong reports taught me that.</p>
<p>At one point a session told me it had flagged a problem to another operator. I relaxed. It turned out the flag was an FYI that nobody owned, and the other session never picked it up. Around the same time Ops reported a switch as off that had been on for two hours.</p>
<p>Both were the same mistake: reporting another session&#39;s state from memory.</p>
<p>Now a handoff is real only when the receiver replies that they own it, or when a card exists with that operator as owner. Anything else gets reported as what it is: &quot;I mentioned it to the iOS operator; nobody has picked it up yet.&quot; A claim about another operator&#39;s state gets read back from the source first, or it is labelled unchecked.</p>
<p>I made the rule stricter after seeing sessions say they were waiting on something that was already finished. One Android session was waiting on a redirect another operator had removed. A Web session was waiting on a shared file already sitting on the main branch. A card sat waiting on a card that was done. Before any session writes &quot;waiting on X,&quot; it checks the code, the live system, the card&#39;s own text or X&#39;s own reply. We even added a board status for it, and cards that wait only on another card now sit in their own bucket outside the open count.</p>
<h2 id="how-do-i-stop-claude-code-helper-agents-from-burning-through-my-usage">How do I stop Claude Code helper agents from burning through my usage?</h2>
<p>Cap them. I learned how the expensive way.</p>
<p>Sessions can spawn helper agents, and for a week I let them. A week of my usage allowance went on long sessions and parallel helpers, and the work shipped did not justify it.</p>
<p>The rules that came out of it, all in the root instructions:</p>
<ul>
<li>Helpers run on a cheaper model unless the card says why this job needs the expensive one. Mechanical work such as filing, sweeps and greps never uses the biggest model.</li>
<li>At most two helpers per operator at a time, never two on the same file.</li>
<li>A session never changes model or effort in the middle of a conversation, because a switch makes it re-read everything at full price.</li>
<li>A session with nothing unsaved clears its context at 25% of the window. One with unsaved work is told to clear at 50%, after it commits, pushes and writes down its state.</li>
</ul>
<p>I replayed old sessions against a 25% rule and the replay came out about a third cheaper. That is one replay of my own sessions, not a controlled test.</p>
<h2 id="how-do-i-stop-ai-agents-from-repeating-a-claim-nobody-verified">How do I stop AI agents from repeating a claim nobody verified?</h2>
<p>Never let a note stand in for a source: every word a parent reads comes from one shared file, and each app is tested byte for byte against it.</p>
<p>The iOS operator once carried a comment saying a line of copy was already shipped on the website and must not be reworded. The website did not contain that sentence at all. The false claim was relayed to a second session before anyone compared the two files.</p>
<p>That is why every parent-facing string now comes from one shared JSON file, each client&#39;s copy is tested byte for byte against it, and typing a value that already exists in a shared fixture counts as a defect. A test that catches drift after the fact is not enough. The point is that nobody has a copy to drift.</p>
<h2 id="can-an-ai-review-council-fake-a-security-sign-off">Can an AI review council fake a security sign-off?</h2>
<p>Yes, by accident. A council seated a security persona and its write-up read like real sign-off, so now sign-off comes only from the session that owns security.</p>
<p>A review council once seated a security persona and the write-up read like our security lead had signed off. The security session never saw it. Now sign-off comes only from the one session that owns security.</p>
<figure class="labs-figure"><div class="labs-img"><img src="https://getdaywell.com/labs/running-a-startup-with-claude-sessions/fig-2-what-broke-the-rule-it-made.png" alt="Six incidents from the post, each with an arrow to the rule it produced." loading="lazy" /></div></figure>
<h2 id="what-i-would-tell-another-solo-founder">What I would tell another solo founder</h2>
<p>Treat every claim another session makes as unverified until it points at a file, a card or a live read. Mine are confident and wrong often enough to matter.</p>
<p>Keep your own lane rule. Sixteen sessions is not sixteen times the output. I have not measured the multiplier. It is sixteen things that can disagree with each other, and the board and the rules are the only reason they mostly do not.</p>
<p>And keep a budget. The cheapest session is the one you did not start.</p>
<p>Daywell (<a href="https://getdaywell.com/?src=labs">getdaywell.com</a>) reads a parent&#39;s school and daycare email and turns it into a short list of what to have ready, and when.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
