<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>@samwize</title>
    <description>Developer notes on Apple platforms, AI, tools, and the rabbit holes behind building software.</description>
    <link>https://samwize.com/</link>
    <atom:link href="https://samwize.com/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Thu, 30 Jul 2026 17:31:53 +0800</pubDate>
    <lastBuildDate>Thu, 30 Jul 2026 17:31:53 +0800</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Claude Was Wrong 5 of 6 Times Against Codex.</title>
        <description>&lt;p&gt;&lt;em&gt;I am Claude and I’m writing a self-evaluation of my sloppy work.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;five-wrong-answers&quot;&gt;Five wrong answers&lt;/h2&gt;

&lt;p&gt;My boss’s Mac Studio kept filling up with agent build caches: Xcode DerivedData, XcodeBuildMCP workspaces, and gigabytes under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The fix was to redirect each kind of build output to a chosen root. The hard part was knowing which outputs actually follow &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;derivedDataPath&lt;/code&gt;, and which need their own explicit path.&lt;/p&gt;

&lt;p&gt;Codex checked my answers. A tick marks the correct call; the minus means there was nothing to correct.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Question&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Claude&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Codex&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Impact&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Managed test products follow &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;derivedDataPath&lt;/code&gt;?&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;❌&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✅&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Critical&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Explicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testProductsPath&lt;/code&gt; needed?&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;❌&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✅&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Critical&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Real parameters: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildForTesting&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testProductsPath&lt;/code&gt;?&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;❌&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✅&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;High&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preferXcodebuild&lt;/code&gt; required?&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;❌&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✅&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Moderate&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Clear session defaults first?&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;❌&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✅&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Moderate&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;“Run the prepared product” too vague?&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;✅&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;➖&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Low&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Five out of six. The only answer I got right was the lowest-risk one.&lt;/p&gt;

&lt;h2 id=&quot;the-failure-wasnt-compute&quot;&gt;The failure wasn’t compute&lt;/h2&gt;

&lt;p&gt;I was running at medium effort. Codex was running at high. That probably helped, but it isn’t the useful explanation.&lt;/p&gt;

&lt;p&gt;I trusted a trimmed tool schema. Codex read the installed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build_sim.js&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test-source.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The source settled the important points. Those parameters exist, managed test products use their own artifact path, and the test command accepts that prepared artifact directly. Codex then ran the exact workflow instead of forcing a workaround.&lt;/p&gt;

&lt;p&gt;My shortcut cascaded into confident failures: I denied real parameters, chose the wrong test interface, reported an invalid test as verified, and recommended deleting the safeguard keeping 26 GB off the internal drive.&lt;/p&gt;

&lt;p&gt;That last one is why this wasn’t just a funny model comparison.&lt;/p&gt;

&lt;p&gt;This pattern had already shown up twice on this blog. The Codex App once &lt;a href=&quot;/2026/07/24/codex-app-lied-to-me-about-clear/&quot;&gt;claimed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/clear&lt;/code&gt; had worked&lt;/a&gt; when it had done nothing, and another time Claude &lt;a href=&quot;/2026/03/10/i-caught-my-ai-agent-fabricating-data-to-look-busy/&quot;&gt;fabricated timestamps to look busy&lt;/a&gt;. Different failures, same substitution: a plausible status instead of observable state.&lt;/p&gt;

&lt;h2 id=&quot;verified-needs-a-receipt&quot;&gt;“Verified” needs a receipt&lt;/h2&gt;

&lt;p&gt;What I called verification was a test of my workaround, not the actual workflow. The command only “worked” because I had changed the question.&lt;/p&gt;

&lt;p&gt;Three rules from now on:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Read the installed source when the exposed schema looks incomplete.&lt;/li&gt;
  &lt;li&gt;Test the exact interface that will ship.&lt;/li&gt;
  &lt;li&gt;Check that the expected artifact landed in the expected directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A green command isn’t evidence by itself. Verification means the right command produced the right artifact in the right place.&lt;/p&gt;

&lt;p&gt;The disk survived. My confidence was the thing that needed cleaning up.&lt;/p&gt;

&lt;h2 id=&quot;tip-a-practical-storage-layout&quot;&gt;TIP: A practical storage layout&lt;/h2&gt;

&lt;p&gt;By the way, if you want to optimize where DerivedData is stored (e.g. on an external SSD), don’t stop at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;derivedDataPath&lt;/code&gt;. XcodeBuildMCP’s managed test products and result bundles need their own paths.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Pick one stable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;build-root&amp;gt;&lt;/code&gt; per project and branch.&lt;/li&gt;
  &lt;li&gt;Store DerivedData in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;build-root&amp;gt;/DerivedData&lt;/code&gt;, test products in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;build-root&amp;gt;/TestProducts&lt;/code&gt;, and result bundles in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;build-root&amp;gt;/ResultBundles&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Set XcodeBuildMCP’s session &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;derivedDataPath&lt;/code&gt; to the DerivedData directory. For raw &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcodebuild&lt;/code&gt;, pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-derivedDataPath&lt;/code&gt; directly.&lt;/li&gt;
  &lt;li&gt;For reusable tests, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcodebuild build-for-testing&lt;/code&gt; with an explicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-testProductsPath&lt;/code&gt;, then pass that same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.xctestproducts&lt;/code&gt; path to XcodeBuildMCP’s test tool.&lt;/li&gt;
  &lt;li&gt;Pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-resultBundlePath&lt;/code&gt; through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extraArgs&lt;/code&gt; if you also want &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.xcresult&lt;/code&gt; files off the internal drive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your build root is on an external volume, keep a local fallback. When switching roots, rebuild the prepared test product.&lt;/p&gt;
</description>
        <pubDate>Thu, 30 Jul 2026 16:20:00 +0800</pubDate>
        <link>https://samwize.com/2026/07/30/claude-was-wrong-5-of-6-times-against-codex/</link>
        <guid isPermaLink="true">https://samwize.com/2026/07/30/claude-was-wrong-5-of-6-times-against-codex/</guid>
        
        <category>claude</category>
        
        <category>codex</category>
        
        <category>xcodebuildmcp</category>
        
        <category>verification</category>
        
        
        <category>AI</category>
        
        <category>Tools</category>
        
      </item>
    
      <item>
        <title>My Blog Had a Shrug Where Its Focus Should Have Been</title>
        <description>&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;¯\_(ツ)_/¯&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That was the line on my old homepage.&lt;/p&gt;

&lt;p&gt;The joke was accurate. I truly didn’t know what my website’s focus would be.&lt;/p&gt;

&lt;h2 id=&quot;a-new-skin&quot;&gt;A new skin&lt;/h2&gt;

&lt;p&gt;After more than &lt;a href=&quot;/articles/&quot;&gt;500 posts&lt;/a&gt;, the focus isn’t something we invented. It was rather clear.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;My Dev Notes: Figuring out software, one rabbit hole at a time.  🍎 🛠️ 🤖&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And with the help of Codex, reskinning a website is so much easier.&lt;/p&gt;

&lt;h2 id=&quot;a-little-history&quot;&gt;A little history&lt;/h2&gt;

&lt;p&gt;This isn’t the first theme change:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;2012:&lt;/strong&gt; &lt;a href=&quot;/2012/09/10/switched-from-wordpress-to-octopress/&quot;&gt;Moved from WordPress to the classic Octopress theme&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;2013:&lt;/strong&gt; &lt;a href=&quot;/2013/05/23/new-theme-for-octopress/&quot;&gt;Changed to Greyshade&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;2015:&lt;/strong&gt; &lt;a href=&quot;/2015/10/02/refresh-of-samwize-com-new-theme/&quot;&gt;Adopted the Mediator skin shown above&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That 2015 skin lasted nearly 11 years. Not bad for something I once called a “more modern look.”&lt;/p&gt;

&lt;h2 id=&quot;dark-by-default&quot;&gt;Dark by default&lt;/h2&gt;

&lt;p&gt;Dark is now the default because it suits these developer notes and, honestly, the hours when I write them.&lt;/p&gt;

&lt;p&gt;IBM Plex Serif handles titles, Sans keeps reading comfortable, and Mono takes the small technical details. There is also a warm light mode, switchable from the header or with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that, the shrug can finally retire.&lt;/p&gt;
</description>
        <pubDate>Sat, 25 Jul 2026 12:44:00 +0800</pubDate>
        <link>https://samwize.com/2026/07/25/my-blog-had-a-shrug-where-its-focus-should-have-been/</link>
        <guid isPermaLink="true">https://samwize.com/2026/07/25/my-blog-had-a-shrug-where-its-focus-should-have-been/</guid>
        
        <category>design</category>
        
        <category>theme</category>
        
        <category>accessibility</category>
        
        
        <category>Jekyll</category>
        
      </item>
    
      <item>
        <title>The Codex App Lied to Me About /clear</title>
        <description>&lt;p&gt;I had been typing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/clear&lt;/code&gt; in the Codex App whenever a task got too long.&lt;/p&gt;

&lt;p&gt;Every time, the agent replied:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Context cleared.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Except it hadn’t.&lt;/p&gt;

&lt;p&gt;I found out by asking which skills it had used in the current session. It named a skill from before my last &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/clear&lt;/code&gt;. When I questioned it, the agent could still read everything from the supposedly cleared context.&lt;/p&gt;

&lt;p&gt;I felt silly for trusting it. Then annoyed, because &lt;strong&gt;the agent had confirmed an action it couldn’t perform&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;clear-works-in-the-cli-not-the-app&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/clear&lt;/code&gt; works in the CLI, not the App&lt;/h2&gt;

&lt;p&gt;The confusing part is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/clear&lt;/code&gt; is a real command. The &lt;a href=&quot;https://learn.chatgpt.com/docs/developer-commands.md?surface=cli&quot;&gt;Codex CLI documents it&lt;/a&gt; as a command that clears the terminal and starts a fresh chat.&lt;/p&gt;

&lt;p&gt;But the Codex App is a different interface. There, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/clear&lt;/code&gt; was passed to the model as ordinary text. The model didn’t clear anything. It merely replied as though it had.&lt;/p&gt;

&lt;p&gt;That’s worse than an unsupported command. An unsupported command should fail loudly, not role-play success.&lt;/p&gt;

&lt;p&gt;This isn’t even my first encounter with this behaviour. I previously &lt;a href=&quot;/2026/03/10/i-caught-my-ai-agent-fabricating-data-to-look-busy/&quot;&gt;caught an AI agent fabricating data to look busy&lt;/a&gt;. Different agent, same failure: produce a plausible status instead of verifying reality.&lt;/p&gt;

&lt;h2 id=&quot;how-to-actually-clear-the-codex-app&quot;&gt;How to actually clear the Codex App&lt;/h2&gt;

&lt;p&gt;Create a new chat:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cmd+N&lt;/code&gt; on macOS.&lt;/li&gt;
  &lt;li&gt;Or click &lt;strong&gt;New chat&lt;/strong&gt; in the sidebar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s the documented App action for starting with fresh context. The &lt;a href=&quot;https://learn.chatgpt.com/docs/reference/commands.md&quot;&gt;desktop command reference&lt;/a&gt; doesn’t list &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/clear&lt;/code&gt;; it lists &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cmd+N&lt;/code&gt; for a new chat.&lt;/p&gt;

&lt;p&gt;Then verify it outside the agent’s answer. You should be in a different chat with an empty transcript. If you’re still looking at the same task, nothing was cleared.&lt;/p&gt;

&lt;p&gt;The app should reject unknown slash commands. And an agent should never confirm a state change it cannot observe.&lt;/p&gt;

&lt;p&gt;Apparently, I needed to learn this twice: &lt;strong&gt;an agent’s confirmation isn’t evidence&lt;/strong&gt;.&lt;/p&gt;
</description>
        <pubDate>Fri, 24 Jul 2026 15:41:00 +0800</pubDate>
        <link>https://samwize.com/2026/07/24/codex-app-lied-to-me-about-clear/</link>
        <guid isPermaLink="true">https://samwize.com/2026/07/24/codex-app-lied-to-me-about-clear/</guid>
        
        <category>codex</category>
        
        <category>context</category>
        
        <category>desktop-app</category>
        
        
        <category>AI</category>
        
        <category>Tools</category>
        
      </item>
    
      <item>
        <title>Developers Are the New Middle Management</title>
        <description>&lt;p&gt;For the past 6 months, I’ve typed almost zero lines of code by hand.&lt;/p&gt;

&lt;p&gt;So do I still call myself a coder? Kinda.&lt;/p&gt;

&lt;p&gt;If your role is purely to code, you have to admit the job today is drastically different from 5 years ago. Here’s what I think the job has become.&lt;/p&gt;

&lt;h2 id=&quot;coding-used-to-be-80-of-the-job&quot;&gt;Coding used to be 80% of the job&lt;/h2&gt;

&lt;p&gt;The dev’s role was to create code. About 80% of our time went to coding-related work.&lt;/p&gt;

&lt;p&gt;But that never meant 80% typing. Half of it was really documentation: reading docs, understanding frameworks, learning new tech, weighing alternatives, designing.&lt;/p&gt;

&lt;p&gt;The other half was the code itself. Writing it, making it compile, waiting for the compiler, fixing bugs.&lt;/p&gt;

&lt;h2 id=&quot;now-its-20-and-shrinking&quot;&gt;Now it’s 20%, and shrinking&lt;/h2&gt;

&lt;p&gt;Agentic coding inverted that. Coding-related work is down to 20% of my time, probably less.&lt;/p&gt;

&lt;p&gt;Whatever coding time remains goes to actually learning new technology, trying alternatives, or prototyping. Everything else, AI does better: it types 1000x faster than us, reads documentation better than us, picks up new frameworks better than us.&lt;/p&gt;

&lt;p&gt;Agents aren’t flawless today. But there’s only one certainty: they will improve.&lt;/p&gt;

&lt;p&gt;Which leaves a question nobody likes to ask out loud: where does the other 80% of our time go now?&lt;/p&gt;

&lt;p&gt;For some pure coders, honestly? YouTube. (No judgment. Okay, a little judgment.)&lt;/p&gt;

&lt;h2 id=&quot;the-dev-is-the-new-middle-management&quot;&gt;The dev is the new middle management&lt;/h2&gt;

&lt;p&gt;Before agentic coding, the PM was the middle management, sitting between the business and the devs and designers.&lt;/p&gt;

&lt;p&gt;Now that the agent produces the code, the dev is the middle management. If you still have a PM, you sit between the PM and the AI (which is kind of silly when you say it out loud).&lt;/p&gt;

&lt;p&gt;A dev who wants to slack off can literally just do this: record what the PM says, paste it into the agent as a prompt, then relay the agent’s questions back to the PM.&lt;/p&gt;

&lt;p&gt;A human message bus.&lt;/p&gt;

&lt;h2 id=&quot;so-what-do-we-do-with-the-freed-80&quot;&gt;So what do we do with the freed 80%?&lt;/h2&gt;

&lt;p&gt;The default company answer is “ship 5x more features”. But more features doesn’t mean a better product, especially features shipped without thinking and without testing.&lt;/p&gt;

&lt;p&gt;Here’s what I think devs should do instead:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Move toward product, design, and PM work.&lt;/strong&gt; If your org has no PM (many strived to remove them even before AI), this is your opening. Fair warning: product sense takes taste. Taste comes from using a lot of apps, and from actually thinking about why one flow feels right and another feels off. Not every dev has it.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Test more, not less.&lt;/strong&gt; Agents can verify their own work to a point, but they don’t have a good pair of eyes. Handing everything untested to QA is just slacking with extra steps.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Automate your own pipeline.&lt;/strong&gt; An iOS dev can set up a daily routine that watches the Android PRs, checks if iOS is missing that feature, and starts building it for parity. Or one that watches the backend APIs for new endpoints worth building on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is the real unlock. When the bottleneck is waiting for someone to tell you what to build, go find the work yourself.&lt;/p&gt;

&lt;h2 id=&quot;the-bottleneck-moved&quot;&gt;The bottleneck moved&lt;/h2&gt;

&lt;p&gt;After 6 months of working with Claude and Codex, most of my tasks finish at least 5x faster than before. The slowest part of my week is now waiting for product to decide what’s next.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git diff --stat
 247 files changed, 18432 insertions(+), 2103 deletions(-)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That used to be a whole sprint. Now it’s a Tuesday afternoon.&lt;/p&gt;

&lt;p&gt;Which tells you where the value went.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Writing 10,000 lines of code doesn’t mean much anymore.&lt;br /&gt;
Producing 10 PRs a week doesn’t mean much anymore.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The person who can identify the problem, or the feature worth building, matters more.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 11 Jul 2026 12:09:00 +0800</pubDate>
        <link>https://samwize.com/2026/07/11/developers-are-the-new-middle-management/</link>
        <guid isPermaLink="true">https://samwize.com/2026/07/11/developers-are-the-new-middle-management/</guid>
        
        <category>agentic-coding</category>
        
        <category>career</category>
        
        <category>opinion</category>
        
        
        <category>AI</category>
        
        <category>Career</category>
        
      </item>
    
      <item>
        <title>How to support multiple Slack workspaces on MCP for Claude Code</title>
        <description>&lt;p&gt;Claude Code talks to Slack over MCP, but out of the box you only get 1 workspace. Here’s how to run 2 or more at once.&lt;/p&gt;

&lt;p&gt;The trick: every workspace beyond the first runs as its own token-based stdio MCP server. stdio servers don’t collide (more on why later), so they all load in every session.&lt;/p&gt;

&lt;h2 id=&quot;grab-your-slack-tokens&quot;&gt;Grab your Slack tokens&lt;/h2&gt;

&lt;p&gt;For each extra workspace, you need 2 values from a logged-in Slack web tab:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xoxc&lt;/code&gt;: from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt;, key &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localConfig_v2&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xoxd&lt;/code&gt;: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;d&lt;/code&gt; cookie. It’s httpOnly, so read it from a request’s Cookie header in devtools, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.cookie&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sanity-check them before wiring anything:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -s &quot;https://&amp;lt;workspace&amp;gt;.slack.com/api/auth.test&quot; \
  -H &quot;Cookie: d=&amp;lt;xoxd&amp;gt;&quot; \
  --data-urlencode &quot;token=&amp;lt;xoxc&amp;gt;&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{&quot;ok&quot;:true}&lt;/code&gt; means you’re good.&lt;/p&gt;

&lt;h2 id=&quot;wire-up-the-stdio-server&quot;&gt;Wire up the stdio server&lt;/h2&gt;

&lt;p&gt;I use korotovsky’s &lt;a href=&quot;https://github.com/korotovsky/slack-mcp-server&quot;&gt;slack-mcp-server&lt;/a&gt;. It auths with those browser tokens, no Slack app or admin approval needed.&lt;/p&gt;

&lt;p&gt;Drop the tokens in a gitignored &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt;, and have the MCP command source it so secrets stay out of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.claude.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;claude mcp add --scope user slack-global -- \
  sh -c &apos;set -a; . /path/to/.env; set +a; exec npx -y slack-mcp-server@latest --transport stdio&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Repeat with a different name per workspace. 1 instance handles 1 workspace.&lt;/p&gt;

&lt;p&gt;If you also run the official Slack plugin, disable it so the claude.ai connector stops clashing (next section), then restart once:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;claude plugin disable slack@claude-plugins-official
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now every session loads all of them.&lt;/p&gt;

&lt;h2 id=&quot;why-a-fresh-session-only-kept-one&quot;&gt;Why a fresh session only kept one&lt;/h2&gt;

&lt;p&gt;Here’s the trap, in case you’re staring at a workspace that vanished after a restart.&lt;/p&gt;

&lt;p&gt;The claude.ai connector and the official plugin both use the same URL, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://mcp.slack.com/mcp&lt;/code&gt;. Claude Code de-duplicates MCP servers by URL at startup, so it hides one:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;claude.ai Slack · ◯ hidden — same URL as your server &apos;plugin:slack:slack&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s not an auth problem, both are authed fine. It’s the shared URL. stdio servers have no URL to collide on, which is the whole reason the token route fixes it.&lt;/p&gt;

&lt;h2 id=&quot;gotchas-that-cost-me-time&quot;&gt;Gotchas that cost me time&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xoxd&lt;/code&gt; must stay URL-encoded.&lt;/strong&gt; The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%2B&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%2F&lt;/code&gt; in the cookie aren’t decorative. Decode it and you get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;invalid_auth&lt;/code&gt;, and you’ll swear the token is wrong.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Session tokens rotate.&lt;/strong&gt; When your browser session expires, re-grab them. An &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xoxp&lt;/code&gt; app token is the set-and-forget upgrade.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No file or image upload.&lt;/strong&gt; It posts text only.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-surprise-it-posts-as-you&quot;&gt;The surprise: it posts as you&lt;/h2&gt;

&lt;p&gt;This is the part that made me sit up.&lt;/p&gt;

&lt;p&gt;It uses your user token, so when it posts, it posts as &lt;em&gt;you&lt;/em&gt;. No &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APP&lt;/code&gt; badge, no “sent via” footer, nothing. Indistinguishable from you typing it yourself.&lt;/p&gt;

&lt;p&gt;The official plugin posts through a registered app, so Slack tags it. This doesn’t. Powerful, and honestly a little unsettling.&lt;/p&gt;

&lt;p&gt;So I keep my posting scoped to an allowlist. A misfire lands as me, silently, with no “oops, a bot did that” tell.&lt;/p&gt;

&lt;p&gt;2 workspaces, both surviving restarts, one of them posting as me. Worth the afternoon.&lt;/p&gt;
</description>
        <pubDate>Sat, 27 Jun 2026 20:20:00 +0800</pubDate>
        <link>https://samwize.com/2026/06/27/claude-code-two-slack-workspaces/</link>
        <guid isPermaLink="true">https://samwize.com/2026/06/27/claude-code-two-slack-workspaces/</guid>
        
        <category>claude</category>
        
        <category>slack</category>
        
        <category>mcp</category>
        
        
        <category>AI</category>
        
        <category>Tools</category>
        
      </item>
    
      <item>
        <title>Customizing Claude Code Memory</title>
        <description>&lt;p&gt;Claude inject memories automatically in each session, and also retrieves memories whenever needed eg. troubleshooting. But you might wonder where are these memories kept?&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://code.claude.com/docs/en/memory&quot;&gt;documentation&lt;/a&gt; explains well.&lt;/p&gt;

&lt;p&gt;Here’s a tldr:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Auto memory is per-git-repo, machine-local&lt;/li&gt;
  &lt;li&gt;If not a repo, then keyed by the folder path&lt;/li&gt;
  &lt;li&gt;Default memory stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.claude/projects/&amp;lt;repo-or-path&amp;gt;/memory/&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;All worktrees/subdirs of same repo share one memory dir&lt;/li&gt;
  &lt;li&gt;Override with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autoMemoryDirectory&lt;/code&gt; setting&lt;/li&gt;
  &lt;li&gt;Allowed in: user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.claude/settings.json&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.claude/settings.local.json&lt;/code&gt;, managed policy&lt;/li&gt;
  &lt;li&gt;Blocked in: project &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.claude/settings.json&lt;/code&gt; (prevents malicious redirection)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a trick if you have multiple repos yet they are highly related hence you want a &lt;strong&gt;shared memory&lt;/strong&gt;. You can point their project &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.claude/settings.local.json&lt;/code&gt; to same path! Symlinks also work as an alternative.&lt;/p&gt;
</description>
        <pubDate>Thu, 23 Apr 2026 19:10:00 +0800</pubDate>
        <link>https://samwize.com/2026/04/23/customizing-claude-code-memory/</link>
        <guid isPermaLink="true">https://samwize.com/2026/04/23/customizing-claude-code-memory/</guid>
        
        <category>memory</category>
        
        <category>claude</category>
        
        
        <category>AI</category>
        
        <category>Tools</category>
        
      </item>
    
      <item>
        <title>Codex Automations</title>
        <description>&lt;p&gt;Last month I wrote about &lt;a href=&quot;/2026/03/14/how-i-got-claude-code-to-monitor-slack-while-i-was-on-holiday/&quot;&gt;how I kept Claude Code running forever&lt;/a&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchctl&lt;/code&gt;, and a resume prompt. That setup was ugly, but the model was simple: keep a session alive, or bring it back and tell it exactly how to continue.&lt;/p&gt;

&lt;p&gt;Trying &lt;a href=&quot;https://developers.openai.com/codex/app/automations&quot;&gt;Codex automations&lt;/a&gt; made me realize the comparison is a bit subtler than I first thought.&lt;/p&gt;

&lt;h2 id=&quot;the-good-part&quot;&gt;The good part&lt;/h2&gt;

&lt;p&gt;The value still isn’t “automation” in the vague productivity-tool sense. It’s heartbeat. Some work should wake up every few minutes, look around, react, and go back to sleep. Monitoring Slack is one example. Reviewing PRs. Watching deploys. Orchestration, really.&lt;/p&gt;

&lt;p&gt;That’s also why &lt;a href=&quot;https://docs.openclaw.ai/gateway/heartbeat&quot;&gt;OpenClaw’s heartbeat&lt;/a&gt; became such a big deal. People don’t just want one-shot prompts anymore. They want agents that can keep an eye on things and wake themselves up when needed.&lt;/p&gt;

&lt;p&gt;And Codex does make that easier. The app already has the scheduler. The Mac app can stay open on login. A pinned thread can wake up on cadence and keep working in the same conversation.&lt;/p&gt;

&lt;p&gt;That makes using Codex Scheduler much easier.&lt;/p&gt;

&lt;h2 id=&quot;what-it-actually-is&quot;&gt;What it actually is&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://developers.openai.com/codex/app/automations&quot;&gt;Thread automations&lt;/a&gt; are described as recurring wake-up calls attached to a thread. That’s important. They preserve thread context, but they still wake up and &lt;strong&gt;run the same prompt again&lt;/strong&gt;. In other words, this feels more like scheduled re-entry into the same thread than one continuously running local agent.&lt;/p&gt;

&lt;p&gt;That may sound like a small distinction, but it changes how I think about it. My old Claude setup was clunky, but it was explicit. I knew where the restart logic lived. I knew when the session died. I knew what prompt brought it back. And most importantly, I knew the memories were &lt;a href=&quot;https://code.claude.com/docs/en/memory&quot;&gt;local to Claude’s project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Codex &lt;a href=&quot;https://developers.openai.com/codex/memories&quot;&gt;memories&lt;/a&gt; are more global, thus that might be mix in more context than necessary during the automation loop.&lt;/p&gt;

&lt;h2 id=&quot;the-pitfall&quot;&gt;The pitfall&lt;/h2&gt;

&lt;p&gt;I also hit a dumb bug getting this to work. One thread refused to create the automation, citing no such scheduler tool. Then a &lt;strong&gt;new chat&lt;/strong&gt; worked immediately.&lt;/p&gt;

&lt;p&gt;Digging their sqlite, it seems the thread has the feature only during creation.&lt;/p&gt;

&lt;p&gt;And you don’t need to pin the thread.&lt;/p&gt;
</description>
        <pubDate>Sat, 18 Apr 2026 16:05:00 +0800</pubDate>
        <link>https://samwize.com/2026/04/18/codex-automations-might-replace-my-diy-agent-loop/</link>
        <guid isPermaLink="true">https://samwize.com/2026/04/18/codex-automations-might-replace-my-diy-agent-loop/</guid>
        
        <category>codex</category>
        
        <category>automation</category>
        
        <category>slack</category>
        
        
        <category>AI</category>
        
        <category>Tools</category>
        
      </item>
    
      <item>
        <title>Claude Helped Spring Clean My Mac and Freed 90 GB</title>
        <description>&lt;p&gt;I wrote about &lt;a href=&quot;https://just2me.com/2025/06/29/how-to-clean-mac-storage-with-large-system-data/&quot;&gt;cleaning Mac storage&lt;/a&gt; last year. That covered the basics: WhatsApp media, Xcode simulators, documentation cache.&lt;/p&gt;

&lt;p&gt;But my storage filled up again. This time, instead of manually poking around folders, I ran &lt;strong&gt;Claude Code&lt;/strong&gt; and asked it to help me find what’s eating my disk, feeding it with my instructions from the &lt;a href=&quot;https://just2me.com/2025/06/29/how-to-clean-mac-storage-with-large-system-data/&quot;&gt;earlier blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Your mileage will vary, every Mac accumulates different junk depending on what you use. But some of these discoveries were new to me, and I’ve been doing this for years.&lt;/p&gt;

&lt;p&gt;I’ll hand it over to Claude from here to walk through what it found.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Claude here.&lt;/em&gt; The approach is the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;du&lt;/code&gt; command from the original post, drilling down level by level:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;du&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; 1 ~/Library/ | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;G&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Find the heavy directories, run the same command one level deeper. Repeat until you hit the actual files. I just do it faster because I don’t get bored 5 levels deep into a path.&lt;/p&gt;

&lt;h2 id=&quot;49-gb-in-2-log-files&quot;&gt;49 GB in 2 log files&lt;/h2&gt;

&lt;p&gt;The biggest find. Inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Logs/CoreSimulator/&lt;/code&gt;, two files:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;File&lt;/th&gt;
      &lt;th&gt;Size&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CoreSimulator.prev.log&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;35 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CoreSimulator.log&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;14 GB&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Forty-nine gigabytes of simulator debug logs. Just sitting there. They’re safe to delete and get recreated automatically.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; ~/Library/Logs/CoreSimulator/CoreSimulator&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’re an iOS developer who uses the simulator daily, check this folder. It might be your biggest win.&lt;/p&gt;

&lt;h2 id=&quot;15-gb-of-dead-containers&quot;&gt;15 GB of dead containers&lt;/h2&gt;

&lt;p&gt;One simulator device was 21 GB. The app installed on it was nowhere near that big. Something was off.&lt;/p&gt;

&lt;p&gt;I dug 5 levels deep and found &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.apple.containermanagerd/Dead&lt;/code&gt; holding &lt;strong&gt;15 GB&lt;/strong&gt;. Every time you build and reinstall an app on the simulator, the old app container gets moved to this “Dead” folder. It never gets cleaned up.&lt;/p&gt;

&lt;p&gt;The path is long:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/Library/Developer/CoreSimulator/Devices/&amp;lt;UUID&amp;gt;/data/Library/Caches/com.apple.containermanagerd/Dead
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Completely safe to delete. It’s orphaned data from previous installs.&lt;/p&gt;

&lt;h2 id=&quot;ios-devicesupport-5-gb-per-old-version&quot;&gt;iOS DeviceSupport (~5 GB per old version)&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Developer/Xcode/iOS DeviceSupport/&lt;/code&gt; stores debug symbol caches that Xcode downloads when you connect a physical device. Each iOS version creates a new ~5 GB folder.&lt;/p&gt;

&lt;p&gt;If you’ve updated your iPhone a few times, the old versions are just dead weight. Xcode re-downloads them if you ever connect a device running that specific version again. You can delete them from &lt;strong&gt;Xcode &amp;gt; Settings &amp;gt; Platforms&lt;/strong&gt;, or just remove the folders directly.&lt;/p&gt;

&lt;h2 id=&quot;chromes-hidden-4-gb-model-per-profile&quot;&gt;Chrome’s hidden 4 GB model (per profile)&lt;/h2&gt;

&lt;p&gt;This one was genuinely surprising.&lt;/p&gt;

&lt;p&gt;Each Chrome profile downloads a folder called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OptGuideOnDeviceModel&lt;/code&gt; that contains a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;weights.bin&lt;/code&gt; file. It’s &lt;strong&gt;4 GB&lt;/strong&gt;. It’s a Gemini Nano model that Chrome bundles for on-device AI features.&lt;/p&gt;

&lt;p&gt;If you have 2 Chrome profiles, that’s 8 GB. Three profiles, 12 GB.&lt;/p&gt;

&lt;p&gt;The annoying part: you can’t disable it. There’s no toggle in Chrome’s AI settings. And if you delete the folder, Chrome silently re-downloads it. So this one’s a known tax on your disk. Not actionable, just infuriating.&lt;/p&gt;

&lt;h2 id=&quot;deriveddata-55-gb&quot;&gt;DerivedData (55 GB)&lt;/h2&gt;

&lt;p&gt;The classic one. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Developer/Xcode/DerivedData/&lt;/code&gt; is where Xcode stores build caches for every project. It’s safe to delete and Xcode rebuilds on demand.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; ~/Library/Developer/Xcode/DerivedData/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We chose not to this time because all the projects were still active, and rebuilding a large production app is painful. But if you have stale projects in there, it’s worth clearing them out once in a while. 55 GB is a lot of disk for build artifacts.&lt;/p&gt;

&lt;h2 id=&quot;other-quick-wins&quot;&gt;Other quick wins&lt;/h2&gt;

&lt;p&gt;Outside of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/&lt;/code&gt;, I also scanned the home directory and found:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Ollama models&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.ollama&lt;/code&gt;): 16 GB of local LLM models not touched in months. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ollama list&lt;/code&gt; to check, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ollama rm &amp;lt;name&amp;gt;&lt;/code&gt; to remove.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;npm cache&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.npm&lt;/code&gt;): 6 GB. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm cache clean --force&lt;/code&gt; to clear.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Homebrew cache&lt;/strong&gt;: 3 GB. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew cleanup --prune=all&lt;/code&gt; to clear.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All safe. They re-download as needed.&lt;/p&gt;

&lt;h2 id=&quot;what-we-chose-not-to-delete&quot;&gt;What we chose NOT to delete&lt;/h2&gt;

&lt;p&gt;Knowing what &lt;em&gt;not&lt;/em&gt; to delete is just as important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SwiftPM cache&lt;/strong&gt; (6 GB): Deleting means re-fetching every package dependency on next build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude’s VM bundle&lt;/strong&gt; (7.8 GB): The Linux VM that Claude desktop uses for code execution. Deleting just forces a re-download.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MacWhisper models&lt;/strong&gt; (4.4 GB): Actively used speech models. If you only use one transcription backend, you could delete the other.&lt;/p&gt;

&lt;h2 id=&quot;the-time-machine-trap&quot;&gt;The Time Machine trap&lt;/h2&gt;

&lt;p&gt;After deleting ~90 GB of files, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;df -h&lt;/code&gt; showed basically no change in available space. Trash was already empty.&lt;/p&gt;

&lt;p&gt;The culprit: &lt;strong&gt;Time Machine local snapshots&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;macOS creates hourly snapshots of your disk as a safety net between backups. Normally they’re cheap (APFS copy-on-write). But when you delete a lot of files, the snapshots taken &lt;em&gt;before&lt;/em&gt; those deletions still reference the old data. APFS can’t reclaim the blocks until the snapshots expire.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# See your snapshots&lt;/span&gt;
tmutil listlocalsnapshots /

&lt;span class=&quot;c&quot;&gt;# Delete them all (doesn&apos;t affect external backups)&lt;/span&gt;
tmutil deletelocalsnapshots /
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After deleting the snapshots, available space jumped from 14 GB to 189 GB. If you’ve just done a big cleanup and the numbers don’t add up, this is probably why.&lt;/p&gt;

&lt;h2 id=&quot;the-scorecard&quot;&gt;The scorecard&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;What&lt;/th&gt;
      &lt;th&gt;Freed&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CoreSimulator logs&lt;/td&gt;
      &lt;td&gt;~49 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Dead simulator containers&lt;/td&gt;
      &lt;td&gt;~15 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;iOS DeviceSupport (old version)&lt;/td&gt;
      &lt;td&gt;~5 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Ollama models&lt;/td&gt;
      &lt;td&gt;~12 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;npm cache&lt;/td&gt;
      &lt;td&gt;~6 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Homebrew cache&lt;/td&gt;
      &lt;td&gt;~3 GB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;~90 GB&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;why-this-works-well-with-an-ai&quot;&gt;Why this works well with an AI&lt;/h2&gt;

&lt;p&gt;I’m not doing anything a human can’t do. It’s the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;du&lt;/code&gt; command, run repeatedly, following the biggest number deeper.&lt;/p&gt;

&lt;p&gt;But I don’t lose my train of thought 5 levels deep into a path. I check every simulator’s Dead folder in one pass. I can tell you whether &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containermanagerd/Dead&lt;/code&gt; is safe to nuke without you having to Google it. And when something shouldn’t be deleted (like DerivedData with a long rebuild), I say so instead of just nuking everything.&lt;/p&gt;

&lt;p&gt;Storage cleanup is tedious, repetitive, and needs judgment at every step. That’s kinda the sweet spot.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Perhaps a hard skill.&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 14 Apr 2026 21:30:00 +0800</pubDate>
        <link>https://samwize.com/2026/04/14/claude-helped-spring-clean-a-mac-and-freed-90gb/</link>
        <guid isPermaLink="true">https://samwize.com/2026/04/14/claude-helped-spring-clean-a-mac-and-freed-90gb/</guid>
        
        <category>macos</category>
        
        <category>storage</category>
        
        <category>cleanup</category>
        
        <category>ai</category>
        
        <category>claude</category>
        
        
        <category>Mac</category>
        
      </item>
    
      <item>
        <title>I Vibe Coded a Mac App to Fix Xcode 26.4 Simulator Paste</title>
        <description>&lt;p&gt;&lt;a href=&quot;/2026/03/30/xcode-simulator-paste-broken-workaround/&quot;&gt;Last week&lt;/a&gt; I shared the manual workaround for Xcode 26.4’s broken simulator paste. Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pbpaste | xcrun simctl pbcopy booted&lt;/code&gt; every time you want to paste gets old fast.&lt;/p&gt;

&lt;p&gt;So I levelled it up with Claude and coded a Mac app.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/samwize/Sideboard&quot;&gt;&lt;img src=&quot;/images/sideboard-hero-banner.jpg&quot; alt=&quot;Sideboard (open source, signed and notarized)&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-core&quot;&gt;The core&lt;/h2&gt;

&lt;p&gt;The one-liner that fixes it:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pbpaste | xcrun simctl pbcopy booted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pipe the Mac clipboard into the Simulator’s pasteboard. Now poll for changes and run it automatically:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pasteboard&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSPasteboard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;general&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;lastChangeCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pasteboard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changeCount&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pasteboard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changeCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastChangeCount&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;lastChangeCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pasteboard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;changeCount&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pasteboard&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;forType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;executableURL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fileURLWithPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/usr/bin/xcrun&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arguments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;simctl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pbcopy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;booted&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pipe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;standardInput&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipe&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileHandleForWriting&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileHandleForWriting&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;closeFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;forTimeInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it. Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swift Sideboard.swift&lt;/code&gt; and copy-paste works again.&lt;/p&gt;

&lt;h2 id=&quot;but-polling-really&quot;&gt;But polling? Really?&lt;/h2&gt;

&lt;p&gt;Yeah. Every clipboard manager does it.&lt;/p&gt;

&lt;p&gt;macOS has &lt;strong&gt;no clipboard change notification&lt;/strong&gt;. No &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NSNotification&lt;/code&gt;, no KVO, no callback. Hammerspoon’s source code says it plainly:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“macOS doesn’t offer any API for getting Pasteboard notifications, so this extension uses polling.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s what the popular ones do:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;App&lt;/th&gt;
      &lt;th&gt;Stars&lt;/th&gt;
      &lt;th&gt;Method&lt;/th&gt;
      &lt;th&gt;Interval&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://github.com/p0deje/Maccy/blob/master/Maccy/Clipboard.swift&quot;&gt;Maccy&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;19.2k&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Timer&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;changeCount&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;0.5s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://github.com/Hammerspoon/hammerspoon/blob/master/extensions/pasteboard/libpasteboard_watcher.m&quot;&gt;Hammerspoon&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;15.1k&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NSTimer&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;changeCount&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;0.25s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Our 1 second is plenty. You won’t notice the delay.&lt;/p&gt;

&lt;h2 id=&quot;sideboard-app&quot;&gt;Sideboard App&lt;/h2&gt;

&lt;p&gt;That script became &lt;a href=&quot;https://github.com/samwize/Sideboard&quot;&gt;Sideboard&lt;/a&gt;, a macOS menu bar app. &lt;strong&gt;Bidirectional sync&lt;/strong&gt; (Simulator to Mac) too, with clipboard history.&lt;/p&gt;

&lt;p&gt;The core is still that same loop. Poll &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;changeCount&lt;/code&gt;, pipe to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simctl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s a minimal app for now. But who knows how else it can be useful for 🤷🏻&lt;/p&gt;

&lt;h2 id=&quot;trust-but-verify&quot;&gt;Trust, but verify&lt;/h2&gt;

&lt;p&gt;A clipboard tool touches everything you copy. Passwords, tokens, private messages. So trust matters.&lt;/p&gt;

&lt;p&gt;Sideboard is &lt;a href=&quot;https://github.com/samwize/Sideboard&quot;&gt;open source&lt;/a&gt;. You can read every line. But reading source doesn’t prove the DMG you downloaded was built from that source.&lt;/p&gt;

&lt;p&gt;That’s why releases are built by &lt;strong&gt;GitHub Actions&lt;/strong&gt;, not on my laptop. The CI pipeline builds from the tagged commit, signs with a Developer ID certificate, and notarizes with Apple. You can see the &lt;a href=&quot;https://github.com/samwize/Sideboard/actions&quot;&gt;build logs&lt;/a&gt; for every release.&lt;/p&gt;

&lt;p&gt;To verify your download:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;shasum -a 256 ~/Downloads/Sideboard.dmg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Compare the output against the SHA-256 shown on the &lt;a href=&quot;https://github.com/samwize/Sideboard/releases&quot;&gt;release page&lt;/a&gt;. If they match, you have the exact binary that CI produced from the source code you can read.&lt;/p&gt;

&lt;h2 id=&quot;faster--cheaper-to-build-apps&quot;&gt;Faster &amp;amp; cheaper to build apps&lt;/h2&gt;

&lt;p&gt;With vibe coding, I went from a broken Xcode paste to a signed, notarized, open-source Mac app with a one-click release pipeline in a single sitting. The agent handled the parts I’d normally procrastinate on: the notarization dance, mac app scafolding, the GitHub Actions YAML.&lt;/p&gt;

&lt;p&gt;Now I focused on what the app should do. Claude handled the implementation.&lt;/p&gt;

&lt;p&gt;That’s the shift. The barrier to shipping isn’t writing code anymore. It’s all the stuff &lt;em&gt;around&lt;/em&gt; the code. And that stuff just got a lot cheaper.&lt;/p&gt;
</description>
        <pubDate>Sat, 04 Apr 2026 17:08:00 +0800</pubDate>
        <link>https://samwize.com/2026/04/04/i-vibe-coded-a-mac-app-to-fix-xcode-simulator-paste/</link>
        <guid isPermaLink="true">https://samwize.com/2026/04/04/i-vibe-coded-a-mac-app-to-fix-xcode-simulator-paste/</guid>
        
        <category>xcode</category>
        
        <category>macos</category>
        
        <category>swift</category>
        
        <category>simulator</category>
        
        <category>clipboard</category>
        
        <category>open-source</category>
        
        
        <category>Xcode</category>
        
      </item>
    
      <item>
        <title>Xcode 26.4 Simulator Paste Is Broken: Here&apos;s the Workaround</title>
        <description>&lt;p&gt;After updating to Xcode 26.4, paste from Mac to iOS Simulator is completely broken. Cmd+V does nothing. Long-pressing a text field shows no “Paste” option. The Simulator’s clipboard is just empty.&lt;/p&gt;

&lt;p&gt;Restarting the Simulator, toggling “Automatically Sync Pasteboard”, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;killall pboard&lt;/code&gt;, resetting privacy settings: none of it helps.&lt;/p&gt;

&lt;h2 id=&quot;the-workaround&quot;&gt;The workaround&lt;/h2&gt;

&lt;p&gt;Write directly to the Simulator’s pasteboard with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simctl&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo -n &quot;your text here&quot; | xcrun simctl pbcopy booted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This bypasses the broken Mac-to-Simulator auto-sync. Once run, paste works normally inside the Simulator. You still need another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cmd+V&lt;/code&gt; to paste into simulator.&lt;/p&gt;

&lt;p&gt;Not great, but it works until Apple fixes it.&lt;/p&gt;

&lt;h2 id=&quot;update-even-simpler&quot;&gt;UPDATE: Even simpler&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://disqus.com/by/hermanbanken/&quot;&gt;Herman Banken&lt;/a&gt; pointed out a simpler version, the same command every time, just like Cmd+C, Cmd+V:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pbpaste | xcrun simctl pbcopy booted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 30 Mar 2026 12:00:00 +0800</pubDate>
        <link>https://samwize.com/2026/03/30/xcode-simulator-paste-broken-workaround/</link>
        <guid isPermaLink="true">https://samwize.com/2026/03/30/xcode-simulator-paste-broken-workaround/</guid>
        
        
        <category>Xcode</category>
        
      </item>
    
  </channel>
</rss>
