Skip to content

Document scheduling Passes on macOS with a LaunchAgent - #1509

Merged
JacobStephens2 merged 2 commits into
mainfrom
issue-1501
Oct 28, 2026
Merged

JacobStephens2 merged 2 commits into
mainfrom
issue-1501

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

Guide only. A new macOS subsection under On a schedule explains why a Pass started by cron on a Mac fails: gh and Claude Code keep their logins in the macOS Keychain, and a cron job runs outside the login session, so it can't read them. The subsection gives a LaunchAgent to use instead.

 ### On a schedule
   crontab example, PATH / cd / log file / logins bullets
-  Any scheduler that runs the command works; a systemd user timer needs lingering…
+  Any scheduler … lingering… On macOS, use a LaunchAgent rather than cron → link
+  #### On macOS, a LaunchAgent
+    cause: Keychain unreadable from cron → unauthenticated gh → the 403 log line, verbatim
+    example plist for a Pickup run (Label, ProgramArguments via /bin/zsh -c, PATH,
+      StartInterval 1800, RunAtLoad), with & escaping and full paths
+    launchctl bootstrap / print / bootout
+    launchd won't overlap a job's starts; only while logged in; neither fires asleep
+    `thirdshift cronjobs` and the Console's Cron jobs page list crontab entries only;
+      last-fired times still show there and in `thirdshift status`
 ### A Pickup run on a schedule
+  On macOS, start it from a LaunchAgent rather than cron → link

site/docs/index.html is regenerated from the guide.

The hand-written site pages were checked and left unchanged, because none of them describes scheduling with cron only:

  • site/how-it-works/ "Scheduled work" says "your operating system's scheduler".
  • site/architecture/ already says "cron or launchd starts Passes".
  • site/index.html says only that the operating system schedules Passes.

Evidence

  • Before: the guide's scheduling sections showed only Linux crontab lines. Searching it for API rate limit exceeded, Keychain or LaunchAgent found nothing.
  • After: rg -n "API rate limit exceeded|Keychain|LaunchAgent" docs/guide.md finds the new subsection.
  • Plist check: this host is Linux, so plutil isn't available. The example was taken out of the guide and checked three ways instead:
    • plistlib.load parses it, with all five keys.
    • xmllint --noout passes.
    • bash -n passes on the decoded -c string, cd /Users/you/repos/widgets && thirdshift pickup base main >> /Users/you/.thirdshift/logs/cron.log 2>&1.
  • Tests: scripts/affected-tests origin/main passes: npm test --prefix site, cargo test --test share_card --test site_deploy and cargo test --bin thirdshift prompts_page. cd site && npm run docs:check passes.

Merge Danger

Door: two-way. It changes documentation only.

Blast Radius: docs. Only docs/guide.md and the generated /docs/ page change.

Review

The Standards axis raised 7 findings, all judgement calls on prose. All 7 were addressed:

  • the duplicated Keychain explanation in the Pickup section is now a pointer;
  • the PATH and log bullets keep only what differs from cron;
  • "Pass" is written one way;
  • Claude Code is named;
  • the lock is linked;
  • the voice is fixed;
  • the shared cron.log name is explained.

The Spec axis raised 3. The > wording nit was fixed: the plist now writes > bare, which XML allows, and the bullet says only & must be escaped.

Review coverage: every changed file was read. The Standards reviewer covered site/docs/index.html as confirmed generated output: it is generated from docs/guide.md, npm run docs:check passes, and the reviewer inspected its escaping and anchors in the diff. The Spec reviewer read it.

Unaddressed findings

Standards: none.

Security: none. The change is documentation only: docs/guide.md and the generated site/docs/index.html, whose new HTML is fully escaped. The example plist and commands run only as the user who installs them, the sample IP is from the 203.0.113.0/24 documentation range, and the paths are placeholders. No trust boundary is touched, so no proof-of-concept test applies.

Spec:

  • More scheduling examples than the spec asked for. The note adds an Architect-run variant (StartInterval 300) and a nightly StartCalendarInterval, where the spec asks for "a complete example plist for a Pickup run". Kept: the subsection sits in On a schedule, which is about Architect runs, and its line "Write one LaunchAgent per line you would have put in a crontab" needs the Architect and nightly equivalents to be complete. The reviewer recommended keeping it.
  • Extra bullets beyond the spec's list. These cover RunAtLoad at login, mkdir -p ~/.thirdshift/logs, and Homebrew's /opt/homebrew/bin in PATH. Kept: each is something a reader needs for the example to work, it mirrors the existing cron bullets, and the reviewer judged all of them accurate.

Closes #1501

Built with claude 2.1.312 · claude-opus-5-5 · high

Security review outcome

No unaddressed introduced findings.

cron jobs on macOS run outside the login session and can't read the
Keychain logins of gh and claude, so passes fail with GitHub's
unauthenticated rate limit. Show a LaunchAgent for a Pickup run, with
the launchctl commands, and point the Pickup schedule section to it.
Point the Pickup section to the note rather than repeating it, keep only
what differs from cron in the PATH and log bullets, link the lock, name
Claude Code, and write > bare in the plist since XML allows it.
@JacobStephens2
JacobStephens2 merged commit c7edca7 into main Oct 28, 2026
7 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-1501 branch October 28, 2026 02:28
@JacobStephens2 JacobStephens2 mentioned this pull request Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document scheduling Passes on macOS with a LaunchAgent, since cron can't read Keychain logins

1 participant