Repository navigation
fix(t3x): derive and verify the autobuild agent's PATH, and correct the runbook interval (#41) - #77
Merged
Merged
Conversation
…he runbook interval (#41) Two traps from #41 that bite before any of the install-race work, both fork-owned (`scripts/t3x/**`, `docs/t3x/**`), zero upstream seam. ## 1. Regenerating the plist would silently downgrade the toolchain `--print-launchd` hardcoded its PATH. The live agent's is wider — it carries the fnm node directory, which the generator never knew about — so anyone following the runbook to regenerate the agent would quietly hand it a different PATH than the one that has been building successfully. `c33e5a361` already back-ported the `~/.cargo/bin` half of that gap, and that is the half with a loud failure: no cargo means `spawn cargo ENOENT` every tick, backing off forever. The node half is worse precisely because it is quiet. On this machine the hardcoded list still resolves node — to Homebrew's, which is v26.7.0, while `package.json`'s `engines.node` is `^24.13.1`. The build does not break. It succeeds against a toolchain nobody chose, and installs the result. So the PATH is now derived and then proved: - `derive_agent_path` resolves node, pnpm, cargo and git, following each symlink to a stable directory. That matters: a version manager's shim lives in a per-shell directory (fnm's is `~/.local/state/fnm_multishells/<pid>_<stamp>/bin`) that is deleted when the shell exits, so a shim path baked into a plist is a PATH entry that stops existing. A directory is added only when nothing already on the list provides that tool — which is what keeps corepack's `pnpm`, whose symlink lands in `.../corepack/dist` rather than a bin dir, from contributing an ephemeral entry: the node installation's own bin holds the shim and is already there. - `verify_agent_path` then runs each tool from an EMPTY environment, because that is what launchd gives the job — a check that inherits this shell's PATH proves nothing. It refuses to emit (exit 2) if a tool is unreachable, or if node's major does not match `engines.node`. `--force` emits anyway. On this machine the derived PATH matches the live agent's entry-for-entry. `--diff-launchd` is the missing feedback loop: launchd keeps the copy it was bootstrapped with, so a fix to the emitter never reaches a running agent and nothing reports the gap. It compares settings and ignores comments, so the notes hand-written into the live plist do not read as differences. Also emits `COREPACK_ENABLE_DOWNLOAD_PROMPT=0`, which the live plist has and the generator did not: corepack provides pnpm here against a pinned `packageManager`, and with no tty a download prompt has nobody to answer it. ## 2. The runbook documented a 2-minute loop `--interval`'s default was written as `60` (it is `43200`), the TL;DR said "every 60s", and all three examples passed `--interval 120`. Following them installs a two-minute quit/replace/relaunch cycle on an app hosting live Claude sessions and serving `:3773`. Corrected, and the install examples now show the flags the agent actually runs with, including `--ref origin/main`. Verified under /bin/bash 3.2 (what the plist invokes, and where expanding an empty array under `set -u` is an error): clean emit with zero stderr, `plutil -lint` OK, both refusal paths exit 2 emitting nothing, `--force` overrides, and `--diff-launchd` reports only the differences that are real. Refs #41 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the two items #41 flags as "traps to fix at the same time" — the ones its triage comment says are "docs/script-only, cost nothing in seam surface, and are not superseded" by the update-delivery work. The install-race, relaunch-verification and watchdog items are untouched; those harden a script the fork intends to retire.
Both changes are fork-owned (
scripts/t3x/**,docs/t3x/**). No upstream file is touched, so no new seam-ledger row.1.
--print-launchdwould have silently downgraded the toolchainThe generator hardcoded its PATH. The live agent's is wider — it carries the fnm node directory the generator never knew about — so regenerating the agent by following the runbook hands it a different PATH than the one that has been building successfully for weeks.
c33e5a361already back-ported the~/.cargo/binhalf. That half fails loudly: no cargo meansspawn cargo ENOENTon every tick, backing off forever. The node half is worse because it is quiet. Measured on this machine:/opt/homebrew/bin/node→ v26.7.0package.jsonengines.node^24.13.1~/.local/share/fnm/node-versions/v24.18.0/…→ v24.18.0The build does not fail. It succeeds against a toolchain nobody chose and installs the result.
The fix is to derive it and then prove it.
derive_agent_pathresolvesnode,pnpm,cargo,gitand follows each symlink to a stable directory — a version manager's shim lives in a per-shell directory (fnm:~/.local/state/fnm_multishells/<pid>_<stamp>/bin) that is deleted when that shell exits, so a shim path in a plist is an entry that quietly ceases to exist. A directory is added only when nothing already on the list provides that tool, which is what stops corepack'spnpm— whose symlink lands in.../corepack/dist, not a bin dir — from contributing an ephemeral entry.verify_agent_paththen runs each tool from an empty environment, because that is what launchd gives the job; a check that inherits the current shell's PATH proves nothing. It refuses to emit if a tool is unreachable or if node's major does not matchengines.node.On this machine the derived PATH matches the live agent's entry for entry.
--diff-launchdis the feedback loop that was missing. launchd keeps the copy it was bootstrapped with, so a fix to the emitter never reaches a running agent and nothing anywhere reports the gap — which is how the live plist came to carry a hand-edited PATH the generator did not know about. It compares settings and ignores comments, so notes hand-written into the live plist do not read as differences.Also emits
COREPACK_ENABLE_DOWNLOAD_PROMPT=0, which the live plist has and the generator did not: with no tty, a corepack download prompt has nobody to answer it.2. The runbook documented a two-minute loop
--interval's default was written as60(it is43200), the TL;DR said "every 60s", and all three examples passed--interval 120. Following them installs a two-minute quit/replace/relaunch cycle on an app hosting live Claude sessions and serving:3773. The install examples now also show the flags the agent actually runs with, including--ref origin/main.Verification
Run under
/bin/bash3.2 — what the plist invokes, and where expanding an empty array underset -uis an "unbound variable" error (this bit once during development and is now commented at the capture site):plutil -lintOKcargounreachablev26vsv24--force--diff-launchd--diff-launchdwhen verification failsWhat this does not do
The
KeepAlive/ThrottleInterval, dmg-freshness, atomicwrite_status,hdiutil -readonlyand "reject--relaunchwithout--install" items under #41's Smaller items are still open. So is the install-race work, deliberately.Refs #41
🤖 Generated with Claude Code