From a6ece7bab8d599ca7fda4dc94118b6503c8f3839 Mon Sep 17 00:00:00 2001 From: Kyle Van Essen Date: Sun, 2 Aug 2026 11:17:32 -0700 Subject: [PATCH 1/4] Configure Codex managed worktrees Add checked-in setup, cleanup, and common actions for Codex worktrees. Carry the local signing override into new managed worktrees and make bootstrap trust each checkout before mise reads its config. Validated with ./ide --bootstrap --no-open, ./swiftformat --lint, and ./test (1,622 unit tests and 33 snapshots). --- .codex/environments/environment.toml | 38 ++++++++++++++++++++++++++++ .worktreeinclude | 2 ++ AGENTS.md | 18 +++++++++++++ README.md | 9 +++++++ ide | 6 +++++ 5 files changed, 73 insertions(+) create mode 100644 .codex/environments/environment.toml create mode 100644 .worktreeinclude diff --git a/.codex/environments/environment.toml b/.codex/environments/environment.toml new file mode 100644 index 00000000..82bf82e9 --- /dev/null +++ b/.codex/environments/environment.toml @@ -0,0 +1,38 @@ +# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY +version = 1 +name = "Stuff" + +[setup] +script = ''' +echo "error: Stuff worktree setup is supported on macOS and Linux only" >&2 +exit 1 +''' + +[setup.darwin] +script = "./ide --bootstrap --no-open" + +[setup.linux] +script = "./.cursor/install.sh" + +[cleanup] +script = "" + +[cleanup.darwin] +script = "./simulator --delete" + +[[actions]] +name = "Regenerate project" +icon = "tool" +command = "./ide --no-open" +platform = "darwin" + +[[actions]] +name = "Test affected" +icon = "test" +command = "./test" +platform = "darwin" + +[[actions]] +name = "Lint formatting" +icon = "test" +command = "./swiftformat --lint" diff --git a/.worktreeinclude b/.worktreeinclude new file mode 100644 index 00000000..bd9a4f14 --- /dev/null +++ b/.worktreeinclude @@ -0,0 +1,2 @@ +# Preserve the machine-local signing choice in Codex-managed worktrees. +.mise.local.toml diff --git a/AGENTS.md b/AGENTS.md index fa5c4f8e..82bd3a90 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -553,6 +553,24 @@ Load the [`github-workflow`](../.agents/skills/github-workflow/SKILL.md) skill for PRs, pushes, review feedback, CI, and posting as the user. Always-on: use `gh`; open PRs ready-for-review; mark AI-posted comments. +## Codex worktree specific instructions + +[`.codex/environments/environment.toml`](.codex/environments/environment.toml) +owns setup, cleanup, and toolbar actions for Codex-managed worktrees. Keep it +idempotent and regenerate it through the ChatGPT desktop app's local environment +editor when changing its schema. + +- macOS setup runs `./ide --bootstrap --no-open`; bootstrap trusts the new + checkout's `.mise.toml`, installs pinned tools, syncs agent files, and + generates without opening Xcode. +- Linux setup delegates to [`.cursor/install.sh`](.cursor/install.sh), with the + same platform limits documented below. +- [`.worktreeinclude`](.worktreeinclude) copies only ignored machine-local files + required by a new managed worktree. `AGENTS.override.md` is copied by Codex + automatically and must not be listed there. +- Cleanup uses `./simulator --delete`, which deletes only that checkout's + device and is safe when no device was created. + ## Cursor Cloud specific instructions Cloud agent VMs run **Linux**, not macOS. This repo targets **iOS 26** with diff --git a/README.md b/README.md index 5f1e8f3e..3970b83f 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,13 @@ same simulator. `./simulator --list` shows them with their owning checkouts and `./simulator --prune` (`--dry-run` to preview) cleans up after a checkout you deleted; see `./simulator --help`. +Codex-managed worktrees use the checked-in local environment at +`.codex/environments/environment.toml`. On macOS it runs +`./ide --bootstrap --no-open`, offers project generation, affected tests, and +format lint as toolbar actions, and removes only that checkout's simulator on +cleanup. `.worktreeinclude` copies the gitignored `.mise.local.toml` signing +override from the source checkout into each new managed worktree. + Where's production architecture is checked with Bumper Bowling through the root Swift package: @@ -126,6 +133,8 @@ Tuist.swift Tuist configuration .mise.toml Pins the Tuist, SwiftFormat, and Ruby versions .mise.local.toml Local mise overrides, gitignored (e.g. TUIST_DEVELOPMENT_TEAM) .swiftformat SwiftFormat rules +.codex/ Codex managed-worktree setup, cleanup, and actions +.worktreeinclude Ignored local files copied into Codex-managed worktrees ide Dev script – bootstrap (mise + tools), hooks, sync-agents, tuist generate swiftformat Run SwiftFormat via mise (default: format `.`) sync-agents Sync AGENTS.md → CLAUDE.md and .claude/skills/ diff --git a/ide b/ide index c416eeab..ad40eb26 100755 --- a/ide +++ b/ide @@ -141,6 +141,12 @@ EOF exit 1 fi + # Every clone and worktree has a distinct config path, and mise refuses to + # read an untrusted .mise.toml. Running this explicit bootstrap is the trust + # boundary, so make fresh checkouts usable without a separate manual step. + echo "==> mise trust" + "$MISE" trust + # Install the pinned tools (Tuist, SwiftFormat, Ruby) from .mise.toml. echo "==> mise install" "$MISE" install From 2bba0f7c9c9fa6877169591045b02c3cb70c5846 Mon Sep 17 00:00:00 2001 From: Kyle Van Essen Date: Sun, 2 Aug 2026 11:40:19 -0700 Subject: [PATCH 2/4] Keep Codex worktrees current with main Add an advisory freshness check to managed-worktree setup and an explicit toolbar action that fast-forwards only checkouts directly behind origin/main. Divergent feature history is left untouched for an explicit merge or rebase. Validated with the exact macOS setup command, an isolated stale-main fast-forward, ./swiftformat --lint, and ./test (1,622 unit tests and 33 snapshots). --- .codex/environments/environment.toml | 9 +++- AGENTS.md | 8 ++- README.md | 15 ++++-- worktree | 77 ++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 8 deletions(-) create mode 100755 worktree diff --git a/.codex/environments/environment.toml b/.codex/environments/environment.toml index 82bf82e9..01b34c62 100644 --- a/.codex/environments/environment.toml +++ b/.codex/environments/environment.toml @@ -9,10 +9,10 @@ exit 1 ''' [setup.darwin] -script = "./ide --bootstrap --no-open" +script = "./worktree --check-main && ./ide --bootstrap --no-open" [setup.linux] -script = "./.cursor/install.sh" +script = "./worktree --check-main && ./.cursor/install.sh" [cleanup] script = "" @@ -20,6 +20,11 @@ script = "" [cleanup.darwin] script = "./simulator --delete" +[[actions]] +name = "Update to latest main" +icon = "run" +command = "./worktree --update-main" + [[actions]] name = "Regenerate project" icon = "tool" diff --git a/AGENTS.md b/AGENTS.md index 82bd3a90..20ec3dbf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,7 +51,7 @@ generating; plain `./ide` fails fast pointing at it. The executables in the repo root are the dev scripts — `ide`, `test`, `swiftformat`, `sync-agents`, `profile`, `icons`, `flaky`, `simulator`, -`xcstrings`, `attribution` — and each takes `--help`. Reach for one rather than +`worktree`, `xcstrings`, `attribution` — and each takes `--help`. Reach for one rather than hand-rolling its job: `test` is the only way tests should be run (see [Running tests](#running-tests)), and `icons`, `attribution`, and `simulator` in particular own state that is easy to corrupt by hand — `./simulator` owns a per-checkout device (see the @@ -565,6 +565,12 @@ editor when changing its schema. generates without opening Xcode. - Linux setup delegates to [`.cursor/install.sh`](.cursor/install.sh), with the same platform limits documented below. +- Setup first runs `./worktree --check-main`, which refreshes `origin/main` and + warns without moving `HEAD` when the selected checkout does not contain it; + an unavailable remote warns without blocking setup. +- The **Update to latest main** action runs `./worktree --update-main`; it only + fast-forwards a checkout directly behind `origin/main` and refuses divergent + history. - [`.worktreeinclude`](.worktreeinclude) copies only ignored machine-local files required by a new managed worktree. `AGENTS.override.md` is copied by Codex automatically and must not be listed there. diff --git a/README.md b/README.md index 3970b83f..45d6fa58 100644 --- a/README.md +++ b/README.md @@ -66,11 +66,15 @@ same simulator. `./simulator --list` shows them with their owning checkouts and deleted; see `./simulator --help`. Codex-managed worktrees use the checked-in local environment at -`.codex/environments/environment.toml`. On macOS it runs -`./ide --bootstrap --no-open`, offers project generation, affected tests, and -format lint as toolbar actions, and removes only that checkout's simulator on -cleanup. `.worktreeinclude` copies the gitignored `.mise.local.toml` signing -override from the source checkout into each new managed worktree. +`.codex/environments/environment.toml`. Setup fetches `origin/main` and warns +without changing the checkout when its `HEAD` does not contain the latest main. +The **Update to latest main** toolbar action safely fast-forwards a checkout +directly behind main and refuses divergent feature history. On macOS the +environment also runs `./ide --bootstrap --no-open`, offers project generation, +affected tests, and format lint actions, and removes only that checkout's +simulator on cleanup. `.worktreeinclude` copies the gitignored +`.mise.local.toml` signing override from the source checkout into each new +managed worktree. Where's production architecture is checked with Bumper Bowling through the root Swift package: @@ -139,6 +143,7 @@ ide Dev script – bootstrap (mise + tools), hooks, sync-agents, swiftformat Run SwiftFormat via mise (default: format `.`) sync-agents Sync AGENTS.md → CLAUDE.md and .claude/skills/ simulator Resolve/create this checkout's simulator, boot it, print its UDID +worktree Check or safely fast-forward a checkout against origin/main profile Report build/test hot spots (see `./profile --help`) flaky Detect flaky tests, update FLAKY_TESTS.md (see `./flaky --help`) FLAKY_TESTS.md Flaky tests and their flake counts (generated by `./flaky`) diff --git a/worktree b/worktree new file mode 100755 index 00000000..19e84eff --- /dev/null +++ b/worktree @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: ./worktree --check-main | --update-main + +Checks whether this checkout contains the latest origin/main, or safely +fast-forwards a checkout that is directly behind it. + +Options: + --check-main Fetch origin/main and warn when HEAD does not contain it. + Never changes HEAD or the working tree; a fetch failure warns + without blocking worktree setup. + --update-main Fetch origin/main and fast-forward only when HEAD is its + ancestor. Refuses divergent history. + -h, --help Show this help. +EOF +} + +if [ "$#" -ne 1 ]; then + usage >&2 + exit 1 +fi + +mode="$1" +main_ref="refs/remotes/origin/main" + +fetch_main() { + git fetch --quiet origin '+refs/heads/main:refs/remotes/origin/main' +} + +short_commit() { + git rev-parse --short=8 "$1" +} + +case "$mode" in + --check-main) + if ! fetch_main; then + echo "warning: could not fetch origin/main; main freshness is unknown" >&2 + exit 0 + fi + + if git merge-base --is-ancestor "$main_ref" HEAD; then + echo "==> Checkout contains latest origin/main ($(short_commit "$main_ref"))" + else + echo "warning: checkout $(short_commit HEAD) does not contain latest origin/main ($(short_commit "$main_ref"))" >&2 + echo "warning: run the 'Update to latest main' action if this checkout should be a current main checkout" >&2 + fi + ;; + --update-main) + echo "==> Fetching origin/main" + fetch_main + + if git merge-base --is-ancestor "$main_ref" HEAD; then + echo "==> Checkout already contains latest origin/main ($(short_commit "$main_ref"))" + exit 0 + fi + + if ! git merge-base --is-ancestor HEAD "$main_ref"; then + echo "error: checkout $(short_commit HEAD) has diverged from origin/main ($(short_commit "$main_ref")); refusing to rewrite or merge feature history" >&2 + echo "error: merge or rebase origin/main explicitly instead" >&2 + exit 1 + fi + + git merge --ff-only "$main_ref" + echo "==> Fast-forwarded checkout to latest origin/main ($(short_commit HEAD))" + ;; + -h|--help) + usage + ;; + *) + echo "error: unknown option '$mode'" >&2 + usage >&2 + exit 1 + ;; +esac From 375240513d01e2291997bbe9ffa1b02b3c68d65a Mon Sep 17 00:00:00 2001 From: Kyle Van Essen Date: Sun, 2 Aug 2026 11:48:51 -0700 Subject: [PATCH 3/4] Remove inaccurate generated-config warning The Codex environment is checked-in source maintained by the repository, so remove the header claiming it is autogenerated and should not be edited. Validated with ./swiftformat --lint and ./test (1,622 unit tests and 33 snapshots). --- .codex/environments/environment.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/.codex/environments/environment.toml b/.codex/environments/environment.toml index 01b34c62..e300c02c 100644 --- a/.codex/environments/environment.toml +++ b/.codex/environments/environment.toml @@ -1,4 +1,3 @@ -# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY version = 1 name = "Stuff" From 389417f49b14f155b1f221dccdab0964d6ce8931 Mon Sep 17 00:00:00 2001 From: Kyle Van Essen Date: Sun, 2 Aug 2026 11:52:02 -0700 Subject: [PATCH 4/4] Make documentation validation proportional Allow pure documentation and comment-only changes to skip checks that cannot exercise them, while preserving targeted validation for semantic configuration, tooling, generated-input, example, and rendered-copy changes. Keep the root contract and repo-owned GitHub and testing skills aligned. Validated with ./sync-agents, git diff --check, and equivalent frontmatter checks for both edited skills. ./swiftformat --lint and ./test were not run because these Markdown-only policy changes are outside their scope. --- .agents/skills/github-workflow/SKILL.md | 5 +++-- .agents/skills/running-tests/SKILL.md | 12 +++++++++++- AGENTS.md | 16 +++++++++++----- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.agents/skills/github-workflow/SKILL.md b/.agents/skills/github-workflow/SKILL.md index 97cf8144..28f9da4b 100644 --- a/.agents/skills/github-workflow/SKILL.md +++ b/.agents/skills/github-workflow/SKILL.md @@ -10,8 +10,9 @@ always-on commit and test invariants — this skill assumes those. - Use the `gh` CLI for all GitHub interaction — PRs, issues, checks, releases, review comments. -- **`./swiftformat --lint` and `./test` are part of "done".** Never push a red - tree. +- Validate in proportion to risk. Pure documentation or comment-only changes + may skip checks that cannot exercise them; record skipped checks in the PR. + Never push a known-red tree. - **Never commit on `main`.** Branch first and keep every commit for one piece of work on that one branch. diff --git a/.agents/skills/running-tests/SKILL.md b/.agents/skills/running-tests/SKILL.md index 3d36366e..1842c4b3 100644 --- a/.agents/skills/running-tests/SKILL.md +++ b/.agents/skills/running-tests/SKILL.md @@ -5,10 +5,20 @@ description: Runs the test suite via ./test, picks the right tier, and manages t How to run tests in this repo. Read root [`AGENTS.md`](../../../AGENTS.md) for always-on rules: **use [`./test`](../../../test)** — never hand-roll `tuist test` -or `xcodebuild`; **`./swiftformat --lint` and `./test` are part of "done".** +or `xcodebuild`; validate in proportion to the change. Canonical flag list: `./test --help`. Rationale for `./test` over alternatives: header comment in [`test`](../../../test). +## Documentation-only changes + +Pure documentation or comment-only changes may skip `./test`. Skip +`./swiftformat --lint` too when the changed files are outside the formatter's +scope. Record skipped checks and the reason in the commit or PR validation. + +Do not classify a semantic change to configuration, scripts, generator inputs, +executable examples, or app-rendered copy as documentation-only. Run the +narrowest applicable checks below instead. + ## Pick a tier Pick the **narrowest tier that covers the change**: diff --git a/AGENTS.md b/AGENTS.md index 20ec3dbf..cadc7049 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -527,8 +527,13 @@ flag is needed there. ## Running tests **Use [`./test`](test)** — the only way to run tests. Never hand-roll `tuist -test` or `xcodebuild`. **`./swiftformat --lint` and `./test` are part of -"done".** +test` or `xcodebuild`. **Validate in proportion to risk:** run +`./swiftformat --lint` when the changed files are in its scope, and run the +narrowest applicable `./test` tier for code, build, tooling, or behavior +changes. Pure documentation or comment-only changes may skip checks that +cannot exercise them; record skipped checks in the commit or PR validation. +Semantic changes to configuration, scripts, generator inputs, executable +examples, or app-rendered copy are not documentation-only. Load the [`running-tests`](../.agents/skills/running-tests/SKILL.md) skill for test tiers, snapshot opt-in, why not `tuist test`, and per-checkout simulator @@ -539,9 +544,10 @@ management (`./simulator` resolves a UDID — never pass a device name to - **Never commit on `main`.** Branch first (`git checkout -b `) and keep every commit for one piece of work on that one branch. -- **`./swiftformat --lint` and `./test` are part of "done".** Never commit a red - tree. Load the [`running-tests`](../.agents/skills/running-tests/SKILL.md) - skill for which tier to run. +- **Validate in proportion to risk.** Follow [Running tests](#running-tests), + never commit a known-red tree, and load the + [`running-tests`](../.agents/skills/running-tests/SKILL.md) skill to choose + the applicable checks. - **Multi-step work lands one commit per step**, so history stays bisectable and can land piecewise — including pure-groundwork steps, which say so in the body. - **Commit when asked, or when working through a plan.** If it's unclear whether