<?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>IdealistSpace Zen Garden</title>
    <link>https://idealistspace.com</link>
    <description>Recent articles from Shao&#39;s Zen Garden on idealistspace.com</description>
    <atom:link href="https://idealistspace.com/rss.xml" rel="self" type="application/rss+xml"/>
    <language>en</language>
    <lastBuildDate>Tue, 27 Oct 2026 00:00:00 GMT</lastBuildDate>
    <generator>ZenMD</generator>
    <item>
      <title>This LMS Doesn&#39;t Exist, But Should It?</title>
      <link>https://idealistspace.com/zengarden/this-lms-doesnt-exist</link>
      <guid>https://idealistspace.com/zengarden/this-lms-doesnt-exist</guid>
      <pubDate>Tue, 27 Oct 2026 00:00:00 GMT</pubDate>
      <description>I&#39;ve wanted to build my own LMS for more than a decade. Here&#39;s one direction for it, as a clickable demo, before I spend all my tokens on it.</description>
      <content:encoded><![CDATA[<p>I've wanted to build my own LMS for more than a decade.</p>
<p>In 2015 I wrote down what it would look like: <a href="https://medium.com/learning-technologies/design-of-instructspace-d44c31f78b27">Design of InstructSpace</a>. I followed and admired Canvas when it was first open sourced, taught online with it as a Free for Teacher user (my university was still on Moodle), then spent over ten years in edtech.</p>
<p>I'm outside academia now, and honestly a bit out of touch. But for the first time it feels like we can actually build it. With AI, exploring a direction costs tokens, not years: you can try whichever direction you like, as long as you have tokens to spend.</p>
<p>This is one of those directions.</p>
<h2 id="contents">Contents</h2>
<ul>
<li><a href="https://idealistspace.com/zengarden/this-lms-doesnt-exist#what-i-wanted-in-2015-and-still-do">What I wanted in 2015, and still do</a></li>
<li><a href="https://idealistspace.com/zengarden/this-lms-doesnt-exist#one-direction-zenlms">One direction: ZenLMS</a>
<ul>
<li><a href="https://idealistspace.com/zengarden/this-lms-doesnt-exist#the-week-first">The week, first</a></li>
<li><a href="https://idealistspace.com/zengarden/this-lms-doesnt-exist#notifications-only-for-what-needs-you">Notifications only for what needs you</a></li>
<li><a href="https://idealistspace.com/zengarden/this-lms-doesnt-exist#discussions-you-can-read">Discussions you can read</a></li>
<li><a href="https://idealistspace.com/zengarden/this-lms-doesnt-exist#feedback-on-the-work-itself">Feedback on the work itself</a></li>
<li><a href="https://idealistspace.com/zengarden/this-lms-doesnt-exist#for-teachers-the-same-page-plus-what-needs-you">For teachers: the same page, plus what needs you</a></li>
<li><a href="https://idealistspace.com/zengarden/this-lms-doesnt-exist#on-a-phone-too">On a phone, too</a></li>
</ul>
</li>
<li><a href="https://idealistspace.com/zengarden/this-lms-doesnt-exist#what-we-have-so-far">What we have so far</a></li>
<li><a href="https://idealistspace.com/zengarden/this-lms-doesnt-exist#before-i-spend-all-my-tokens">Before I spend all my tokens</a></li>
</ul>
<h2 id="what-i-wanted-in-2015-and-still-do">What I wanted in 2015, and still do</h2>
<p>Rereading the InstructSpace doc, most of it still holds:</p>
<ul>
<li><strong>Reading comes first.</strong> I was "unsatisfied with all current LMS typography and readability," and wanted simplicity to be the key: "how could we do more with less?"</li>
<li><strong>Notifications should mean something.</strong> Activity is what happened in a course. A notification is something that needs you. They shouldn't be the same list.</li>
<li><strong>Discussions should stay on topic.</strong> Endlessly nested threads drift away from the main question, so: two levels, no more.</li>
<li><strong>Grading should live with the work,</strong> instead of being a chore of counting who posted what.</li>
</ul>
<h2 id="one-direction-zenlms">One direction: ZenLMS</h2>
<p>This one started as a question on X:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">LMS inspired by Tufte... should i do it?</p>— shao (@randomor) <a href="https://twitter.com/randomor/status/2104271544721829972">September 27, 2026</a></blockquote>
<p>ZenLMS is minimalist and text-first: dense, well-set text, plain lists, as little chrome as possible. Every screen below comes from one made-up course, <em>Reading the City</em>, in Week 5 of 14.</p>
<h3 id="the-week-first">The week, first</h3>
<p><img src="https://idealistspace.com/zengarden/assets/zenlms/course-home.png" alt="The course home for Reading the City, opening on Week 5: the teacher&#x27;s note, readings to tick off, and announcements"></p>
<p>The course home opens on this week: the teacher's note, the readings and tasks you tick off as you go, and what's due. Announcements, discussions, people and files sit further down the same page.</p>
<h3 id="notifications-only-for-what-needs-you">Notifications only for what needs you</h3>
<p><img src="https://idealistspace.com/zengarden/assets/zenlms/notifications.png" alt="The notifications panel: a mention, an answer, returned work and a new module, each with one action" width="440"></p>
<p>The 2015 split, finally drawn: the bell holds only things that ask something of you. Everything else lives in each course's Activity.</p>
<h3 id="discussions-you-can-read">Discussions you can read</h3>
<p><img src="https://idealistspace.com/zengarden/assets/zenlms/discussion.png" alt="A discussion thread: a response with three replies under it, @mentions, and a reply box"></p>
<p>Two levels, like the 2015 doc said: responses, and replies under them. You can quote any passage, @mention people, and write in Markdown.</p>
<h3 id="feedback-on-the-work-itself">Feedback on the work itself</h3>
<p><img src="https://idealistspace.com/zengarden/assets/zenlms/feedback.png" alt="A graded paper with the teaching assistant&#x27;s comments in the margin beside each paragraph"></p>
<p>Comments sit in the margin beside your paper, with the rubric below it. You can reply to each comment, or ask for a regrade.</p>
<h3 id="for-teachers-the-same-page-plus-what-needs-you">For teachers: the same page, plus what needs you</h3>
<p><img src="https://idealistspace.com/zengarden/assets/zenlms/teaching-home.png" alt="The teacher&#x27;s course home: progress on each of this week&#x27;s items, and a Needs you list"></p>
<p>Teachers see the same course home, with edit links, progress on each item, and a <em>Needs you</em> list: posts waiting for feedback, questions with no staff reply, extension and regrade requests, and students who haven't opened the week yet.</p>
<p><img src="https://idealistspace.com/zengarden/assets/zenlms/grading.png" alt="Grading a reflection: the student&#x27;s post in the middle, rubric scores and a comment on the right"></p>
<p>Grading happens next to the work: pick the rubric scores, leave a comment, move on to the next student.</p>
<h3 id="on-a-phone-too">On a phone, too</h3>
<p><img src="https://idealistspace.com/zengarden/assets/zenlms/phone.png" alt="The same course home on a phone" width="300"></p>
<h2 id="what-we-have-so-far">What we have so far</h2>
<p>A month after that first question:</p>
<ul>
<li><strong>36 screens</strong> for students and teachers, from signing in to the gradebook, including quizzes, the calendar, building a module, importing from Canvas, groups and the inbox.</li>
<li><strong>A clickable demo</strong> with every screen linked together: <a href="https://zenlms.idealistspace.com/">zenlms.idealistspace.com</a>. Ticks, filters, drafts and quiz answers work, and stay in your browser. Nothing is sent anywhere.</li>
<li><strong>A 2-minute video</strong>, every shot of it the live demo:</li>
</ul>
<blockquote class="twitter-tweet" data-media-max-width="720"><p lang="en" dir="ltr">I've wanted to build my own LMS for more than a decade. … This is a first take: minimalist and text-first. Nothing real is built yet. It's a clickable design with made-up data. … Watch the 2-minute video and let me know.</p>— shao (@randomor) <a href="https://twitter.com/randomor/status/2112623025439080510">October 20, 2026</a></blockquote>
<p>Nothing real is built yet.</p>
<h2 id="before-i-spend-all-my-tokens">Before I spend all my tokens</h2>
<p>This is one direction. I have a handful of other variations, ranging from promising to insane, that I want to explore too. And even with this one, I'm not sure it's something I want to dive into yet.</p>
<p>So I'm putting it out here before I indulge myself too much: should this exist? What would make you use it, or keep you away?</p>
<p>Tell me <a href="https://x.com/randomor/status/2112623025439080510">on X</a>.</p>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>]]></content:encoded>
    </item>
    <item>
      <title>Access Your Mac and Agents Anywhere in 15 Minutes</title>
      <link>https://idealistspace.com/zengarden/vibe-code-anywhere</link>
      <guid>https://idealistspace.com/zengarden/vibe-code-anywhere</guid>
      <pubDate>Mon, 12 Jan 2026 00:00:00 GMT</pubDate>
      <description>Note: This is expanded from my original tweet.</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>Note:</strong> This is expanded from my <a href="https://x.com/randomor/status/2008334210826989856">original tweet</a>.</p>
</blockquote>
<p>Agents run. You prompt. There is no need to type code line by line. The loop tightens. With this free setup, you can do it from anywhere – in a car line, waiting for your ride, or even in the shower.</p>
<h2 id="contents">Contents</h2>
<ul>
<li><a href="https://idealistspace.com/zengarden/vibe-code-anywhere#level-1-access-your-mac-anywhere">Level 1: Access Your Mac Anywhere</a></li>
<li><a href="https://idealistspace.com/zengarden/vibe-code-anywhere#next-level-continuous-flow">Next Level: Continuous Flow</a></li>
<li><a href="https://idealistspace.com/zengarden/vibe-code-anywhere#level-21-agent-notifications">Level 2.1: Agent Notifications</a></li>
<li><a href="https://idealistspace.com/zengarden/vibe-code-anywhere#level-3-push-notifications">Level 3: Push Notifications</a></li>
<li><a href="https://idealistspace.com/zengarden/vibe-code-anywhere#level-4-preview-your-code">Level 4: Preview Your Code</a></li>
<li><a href="https://idealistspace.com/zengarden/vibe-code-anywhere#bonus-levels">Bonus Levels</a></li>
</ul>
<h2 id="level-1-access-your-mac-anywhere">Level 1: Access Your Mac Anywhere</h2>
<p>Here is the simplest free setup to get this, and this should only take you 15 minutes:</p>
<ol>
<li><strong><a href="https://tailscale.com/">Tailscale</a></strong> for VPN and security. Install on both your phone and Mac.</li>
<li><strong><a href="https://termius.com/">Termius</a></strong> for terminal access with a nice keyboard bar. Install on your phone.</li>
<li>Have your Mac always connected to power with the lid up.</li>
<li>Grab your Mac's Tailscale IP from the Tailscale app on your phone.</li>
<li>Enable <strong>Remote Login</strong> in macOS System Settings/Preferences → Sharing so SSH is allowed.</li>
<li>Connect via SSH to that IP in Termius.</li>
</ol>
<p>Now you can vibe from your phone anywhere.</p>
<h2 id="next-level-continuous-flow">Next Level: Continuous Flow</h2>
<p>What if you want to start your work from your Mac and continue on from your phone? Introducing <strong>tmux</strong>:</p>
<ul>
<li>Just define a bash function <code>aa</code> in your <code>.zshrc</code>:
<pre><code class="language-bash">aa(){ tmux new -A -s work}
</code></pre>
</li>
<li>When you connect, type <code>aa</code> to connect or start the session.</li>
<li>You can also start or reattach to the session from your Mac if you have ongoing agent work.</li>
</ul>
<p>Want your tmux to be prettier? Just do this:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">just typed your tweet into my agent and asked it repro it, and it did, since i haven't used tmux for many years and it's pristine setup, it also taught me how to create panels and setup meta key.<br><br>i don't need your dotfiles, all i need is your ideas, thanks for sharing! <a href="https://t.co/yI7QhkfCpu">pic.twitter.com/yI7QhkfCpu</a></p>— shao (@randomor) <a href="https://twitter.com/randomor/status/2007503764832637059?ref_src=twsrc%5Etfw">January 3, 2026</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Not sure how to navigate with tmux windows/panels or remember all the key bindings?</p>
<ul>
<li>You can just use one window/panel.</li>
<li>Or, you can ask your agent to put all common shortcuts in an info panel and hook it up to <code>ctrl + b + i</code>, so you only have to remember that one.</li>
</ul>
<p><img src="https://idealistspace.com/zengarden/assets/tmux-info.png" alt="tmux info panel"></p>
<h2 id="level-21-agent-notifications">Level 2.1: Agent Notifications</h2>
<p>Want to be notified when your agent needs your attention?</p>
<ul>
<li>Ask your agent to always <strong>"emit a terminal bell (<code>printf '\a'</code>)"</strong> in your <code>http://agents.md</code> when they need your attention or are close to being done.</li>
</ul>
<h2 id="level-3-push-notifications">Level 3: Push Notifications</h2>
<p>Just ask Agents.md to run a script that calls a private Discord channel webhook as above. It's all free. No $500/month Poke or $100/month VM needed.</p>
<h2 id="level-4-preview-your-code">Level 4: Preview Your Code</h2>
<p>For Web Apps:</p>
<ul>
<li>Run your app <code>npm run dev</code> (which may start a server at localhost:8080).</li>
<li>Go to the <strong>Tailscale</strong> app, copy the <strong>MagicDNS</strong> link.</li>
<li>You will get a link like <code>yourmac.tailabcd.ts.net</code>.</li>
<li>Just append the port to the MagicDNS link (e.g., <code>yourmac.tailabcd.ts.net:8080</code>).</li>
<li>Now you can preview anywhere.</li>
</ul>
<p>For iOS Apps:</p>
<ul>
<li>Push the TestFlight to preview (follow my workflow <a href="https://idealistspace.com/zengarden/how-i-build-swift-project-to-testflight">here</a>).</li>
<li>Or, if you are in the same home WiFi, you can ask your agent to build your project, it will run on your iPhone directly (won't work outside of WiFi unfortunately).</li>
</ul>
<h2 id="bonus-levels">Bonus Levels</h2>
<ul>
<li><strong>5x Input Bandwidth</strong>: Want to 5x the input bandwidth instead of just 2 fingers? Buy this keyboard:</li>
</ul>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">I declare this as the best keyboard for <a href="https://twitter.com/daylightco?ref_src=twsrc%5Etfw">@daylightco</a> and <a href="https://twitter.com/thezenjournal?ref_src=twsrc%5Etfw">@thezenjournal</a>….<br>I can finally fire with all ten fingers. Perfect for my new vibe coding setup as well… <a href="https://t.co/tQq7mrZhCo">pic.twitter.com/tQq7mrZhCo</a></p>— shao (@randomor) <a href="https://twitter.com/randomor/status/2008309814058008617?ref_src=twsrc%5Etfw">January 5, 2026</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<ul>
<li><strong>1.2x Typing Speed</strong>: Want to type 1.2x faster with a physical keyboard, or simply want to type in bed (i'd not recommend unless you want to mess up your sleep), or in the shower (which I do recommend, like what's stopping you)? Buy the new fancy <a href="https://clicks.tech/">Clicks</a> keyboard. Don't expect significant speed up though, as you have to learn new muscle memory and you still have to type with your thumb.</li>
</ul>
<p>Termius has claimed the top of my screen time list even with my Mac usage over the weekend. It probably will not last, but I like the option of having it available.</p>]]></content:encoded>
    </item>
    <item>
      <title>Build to TestFlight Anywhere with Xcode Cloud while Maximizing Build Minutes</title>
      <link>https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud</link>
      <guid>https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud</guid>
      <pubDate>Fri, 02 Jan 2026 00:00:00 GMT</pubDate>
      <description>I&#39;ve found the ideal workflow to build to TestFlight anywhere, even when vibe coding on the phone, without ever opening Xcode, while maximizing build minutes.</description>
      <content:encoded><![CDATA[<p>I've found the ideal workflow to build to TestFlight anywhere, even when vibe coding on the phone, without ever opening Xcode, while maximizing build minutes.</p>
<p>This is the release flow I use for Swift native projects like <a href="https://doublememory.com/">DoubleMemory</a>. It decouples <code>main</code> from builds so rapid, small PR merges don’t trigger Xcode Cloud, while giving me maximum control over when to build, when to bump versions, and when to cut releases—without merge conflicts and with automatic artifacts and changelogs.</p>
<h2 id="contents">Contents</h2>
<ul>
<li><a href="https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud#goals-and-constraints">Goals and constraints</a></li>
<li><a href="https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud#branch--trigger-model">Branch + trigger model</a></li>
<li><a href="https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud#annotated-workflow-full-quote-with-comments">Annotated workflow (full quote with comments)</a></li>
<li><a href="https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud#the-github-release-path-preferred">The GitHub Release path (preferred)</a></li>
<li><a href="https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud#the-direct-production-push-path-manual-build">The direct production push path (manual build)</a></li>
<li><a href="https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud#why-this-works-well-for-me">Why this works well for me</a></li>
<li><a href="https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud#skill-definition-codex-format">Skill definition (Codex format)</a></li>
<li><a href="https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud#post-installation-steps">Post-installation steps</a></li>
<li><a href="https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud#script-reference">Script reference</a></li>
<li><a href="https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud#final-words">Final Words</a></li>
<li><a href="https://idealistspace.com/zengarden/build-to-testflight-anywhere-with-xcode-cloud#references">References</a></li>
</ul>
<h2 id="goals-and-constraints">Goals and constraints</h2>
<ul>
<li>Optimize Xcode Cloud build minutes and GitHub Actions trigger time.</li>
<li>Build anytime and anywhere, without be bound to a Mac running on xcode.</li>
<li>Keep production builds clean, deterministic, and fast-forward only.</li>
<li>Let a GitHub Release promote and version the main line without manual merges.</li>
<li>Control when TestFlight builds, version bumps, releases, and changelog generation happen.</li>
<li>Allow the <code>main</code> branch to remain the active merge target for frequent PR merges without triggering unnecessary builds or version bumps.</li>
</ul>
<h2 id="branch--trigger-model">Branch + trigger model</h2>
<ul>
<li><code>production</code> is the only branch that triggers Xcode Cloud pipelines.</li>
<li>A GitHub Release (published) triggers <code>release_sync.yml</code>.</li>
<li><code>release_sync.yml</code> only fast-forwards and promotes <code>main</code> into <code>production</code>, so there are no conflicts.</li>
<li>After promotion, the workflow bumps <code>MARKETING_VERSION</code> on <code>main</code>.</li>
</ul>
<p><img src="https://idealistspace.com/zengarden/assets/screenshots/branch-flow.jpeg" alt="Branch flow"></p>
<h2 id="annotated-workflow-full-quote-with-comments">Annotated workflow (full quote with comments)</h2>
<pre><code class="language-yaml"># .github/workflows/release_sync.yml
name: Release Sync and Version Bump

on:
  release:
    types: [published] # Triggered by publishing a GitHub Release.
  workflow_dispatch: # Manual trigger for maintenance.

permissions:
  contents: write

jobs:
  promote:
    name: Promote main to production and bump version
    runs-on: macos-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Full history needed for fast-forward checks.

      - name: Configure Git user
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

      - name: Fast-forward production from main
        env:
          MAIN_BRANCH: main
          PRODUCTION_BRANCH: production
        run: |
          set -eo pipefail
          git fetch --prune origin
          if git ls-remote --exit-code origin "$PRODUCTION_BRANCH" >/dev/null 2>&#x26;1; then
            git checkout -B "$PRODUCTION_BRANCH" "origin/$PRODUCTION_BRANCH"
          else
            git checkout -B "$PRODUCTION_BRANCH" "origin/$MAIN_BRANCH"
          fi
          git merge --ff-only "origin/$MAIN_BRANCH"
          git push origin "$PRODUCTION_BRANCH:$PRODUCTION_BRANCH"

      - name: Bump MARKETING_VERSION on main
        env:
          MAIN_BRANCH: main
        run: |
          set -eo pipefail
          git checkout -B "$MAIN_BRANCH" "origin/$MAIN_BRANCH"
          swift Scripts/bump_version.swift
          if git status --porcelain | grep .; then
            new_version=$(grep -m1 "MARKETING_VERSION =" DoubleMemory.xcodeproj/project.pbxproj | sed -E 's/.*MARKETING_VERSION = ([0-9.]+);/\1/' | tr -d '[:space:]')
            git commit -am "Bump version to $new_version"
            git push origin "$MAIN_BRANCH:$MAIN_BRANCH"
          else
            echo "No version changes detected; skipping commit."
          fi
</code></pre>
<h2 id="the-github-release-path-preferred">The GitHub Release path (preferred)</h2>
<ol>
<li>Create a GitHub Release (auto-tag on release creation).</li>
<li>The release triggers <code>release_sync.yml</code>:
<ul>
<li>Fast-forward <code>production</code> from <code>main</code>.</li>
<li>Bump the version on <code>main</code> and push.</li>
</ul>
</li>
<li>Xcode Cloud builds from <code>production</code>.</li>
<li>Artifacts are generated and a clean changelog is attached to the release automatically by GitHub.</li>
</ol>
<p><img src="https://idealistspace.com/zengarden/assets/screenshots/release-changelog.jpeg" alt="Release changelog"></p>
<p>This keeps versioning centralized and avoids long-running GitHub Actions builds for every release.
If you prefer to avoid clicking in GitHub, this step can be scripted (for example, via a CLI release command) and still uses the same workflow.</p>
<h2 id="the-direct-production-push-path-manual-build">The direct production push path (manual build)</h2>
<p>Sometimes you need to ship a build to TestFlight without cutting a full GitHub Release—for example, to get a quick internal QA build out the door. Because this path only increments the build number (not the marketing version), App Store Connect usually skips the longer human review that full version bumps require. In practice that means approval in minutes, not the days-long wait that has become common as the review queue fills with AI-generated apps.</p>
<p>A local script handles this by fast-forwarding <code>production</code> from <code>main</code> and pushing directly:</p>
<p><code>Scripts/build_app_store.sh</code>:</p>
<pre><code class="language-bash">#!/usr/bin/env bash

set -euo pipefail

# ... (setup variables)

echo "Fast-forwarding ${PRODUCTION_BRANCH} from ${MAIN_BRANCH}..."
git merge --ff-only "origin/$MAIN_BRANCH"

echo "Pushing ${PRODUCTION_BRANCH} to origin..."
git push origin "$PRODUCTION_BRANCH:$PRODUCTION_BRANCH"

# ...
</code></pre>
<p>This triggers Xcode Cloud immediately because of the push to <code>production</code>.</p>
<h2 id="why-this-works-well-for-me">Why this works well for me</h2>
<ul>
<li>Production stays fast-forward only, so it always builds.</li>
<li>Main stays versioned and clean yet still allows active development and merging of PRs without triggering a build which costs build time.</li>
<li>Xcode Cloud runs only when it matters.</li>
<li>GitHub Actions work is minimal and deterministic.</li>
</ul>
<h2 id="skill-definition-codex-format">Skill definition (Codex format)</h2>
<p>If you want this flow packaged as a Codex skill, the definition lives in a <code>SKILL.md</code> with YAML front matter (this is the actual format used by Codex). For the canonical spec and installer usage, see <code>~/.codex/skills/.system/skill-installer/SKILL.md</code>.</p>
<pre><code class="language-md">---
name: ios-release-pipeline
description: Sets up a GitHub Release → fast-forward production → version bump flow for Xcode Cloud.
metadata:
  short-description: Release sync + version bump for iOS
---

# iOS Release Pipeline Setup

Create a `.github/workflows/release_sync.yml` workflow that:
- Triggers on `release` (published) and `workflow_dispatch`.
- Fast-forwards `production` from `main` and pushes.
- Bumps `MARKETING_VERSION` on `main` and pushes.
- Then generate `Scripts/build_app_store.sh` to fast-forward `production` from `main` and push for manual builds.
</code></pre>
<h2 id="post-installation-steps">Post-installation steps</h2>
<p>On GitHub:</p>
<ol>
<li>Enable Actions read/write permissions.</li>
<li>Publish a Release to trigger the sync.</li>
</ol>
<p>On Xcode Cloud:</p>
<ol>
<li>Start Condition = Branch Changes.</li>
<li>Branch = <code>production</code>.</li>
<li>Optional: add TestFlight Internal Testing.</li>
</ol>
<p>Please check <a href="https://x.com/Dimillian/status/2007107988038054240?s=20">Thomas Ricouard’s flow for TestFlight via Codex Web</a> for detailed instructions.</p>
<h2 id="script-reference">Script reference</h2>
<p>The full script lives at <code>Scripts/build_app_store.sh</code>. It fast-forwards <code>production</code> from <code>main</code> and pushes to origin, and it should be generated by the skill rather than hidden. For version bumping, your LLM should be able to generate a small script that matches your project layout; if you use Tuist, this becomes much simpler than my original setup because I started DoubleMemory before adopting Tuist.</p>
<h2 id="final-words">Final Words</h2>
<p>If this post was helpful, I can share the full Swift native project setup in a separate write-up, including topics like how to setup a Swift project by using Tuist, and how to save debug tokens with xcbeautify. You can follow me on X if you want that next: <a href="https://x.com/randomor">@randomor</a>.</p>
<h2 id="references">References</h2>
<ul>
<li>Thomas Ricouard’s flow for TestFlight via Codex Web: <a href="https://x.com/Dimillian/status/2007107988038054240?s=20">https://x.com/Dimillian/status/2007107988038054240?s=20</a></li>
<li>Paul Solt’s manual archive demo for TestFlight: <a href="https://x.com/PaulSolt/status/2007180161569959998?s=20">https://x.com/PaulSolt/status/2007180161569959998?s=20</a></li>
</ul>
<p>This post is inspired by the ongoing discussion about Codex-to-TestFlight flows in those two recent X posts, and special thanks to Paul for the encouragement to write this post as well as adding the skills definition so everyone can easily integrate this flow into their projects.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
