Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .agents/skills/running-tests/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ because they run serially on separate runners; `--snapshot-shard 1/2` and
`2/2` exist for that topology, not same-machine parallelism. See
[`Shared/SnapshotKitTesting/AGENTS.md`](../../../Shared/SnapshotKitTesting/AGENTS.md).

Every snapshot scope preflights the tracked references before generating or
building. If any are still Git LFS pointer text, `./test` fails with
`git lfs pull` rather than producing misleading "incomparable" results.
Codex-managed macOS setup hydrates them through `./ide --bootstrap --no-open`;
run `git lfs pull` explicitly for a manually-created or interrupted checkout.

## Iterate faster

After a green build:
Expand Down
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -557,8 +557,9 @@ 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.
checkout's `.mise.toml`, installs pinned tools, hydrates every Git LFS object
referenced by the checkout, 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.
- Setup first runs `./worktree --check-main`, which refreshes `origin/main` and
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ Codex-managed worktrees use the checked-in local environment at
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
environment also runs `./ide --bootstrap --no-open`, which hydrates the
checkout's Git LFS snapshot references before generating the project; it offers
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.
Expand Down
10 changes: 10 additions & 0 deletions ide
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,16 @@ EOF
# Install the pinned tools (Tuist, SwiftFormat, Ruby) from .mise.toml.
echo "==> mise install"
"$MISE" install

# Codex may create a managed worktree with LFS smudging disabled, leaving
# snapshot references as pointer text even though git-lfs is installed.
# Bootstrap is already the networked, one-shot setup boundary, and all
# worktrees share the repository's LFS object cache, so hydrate the current
# checkout here deterministically. A failed download must fail setup: an
# apparently ready checkout with pointer-backed references cannot run or
# re-record snapshots honestly.
echo "==> git lfs pull"
git lfs pull
fi

MISE="$(find_mise)"
Expand Down
59 changes: 55 additions & 4 deletions test
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,52 @@ add_scheme() {
SCHEMES+=("$candidate")
}

require_hydrated_snapshot_references() {
local reference lfs_files_json
local pointers=()

if ! command -v git-lfs >/dev/null 2>&1; then
echo "error: snapshot tests require git-lfs; run ./ide --bootstrap." >&2
exit 1
fi

# One stable JSON inventory avoids starting `git-lfs` once per reference —
# hundreds of subprocesses and a multi-second tax on every snapshot run.
# `checkout` distinguishes hydrated full objects from pointer text.
lfs_files_json="$WORKDIR/lfs-files.json"
if ! git lfs ls-files --json >"$lfs_files_json"; then
echo "error: could not inspect Git LFS snapshot references." >&2
exit 1
fi
while IFS= read -r reference; do
[ -n "$reference" ] && pointers+=("$reference")
done < <(LFS_FILES_JSON="$lfs_files_json" python3 - <<'PY'
import json, os, pathlib

inventory = json.loads(pathlib.Path(os.environ['LFS_FILES_JSON']).read_text())
for item in inventory['files']:
name = item['name']
if '/__Snapshots__/' in name and name.endswith('.png') and not item['checkout']:
print(name)
PY
)

if [ "${#pointers[@]}" -eq 0 ]; then
return
fi

echo "error: ${#pointers[@]} snapshot reference(s) are Git LFS pointer files, not PNGs." >&2
echo " Snapshot comparisons would be incomplete or incomparable." >&2
echo " Hydrate this checkout, then retry:" >&2
echo " git lfs pull" >&2
echo " First unresolved references:" >&2
printf ' %s\n' "${pointers[@]:0:5}" >&2
if [ "${#pointers[@]}" -gt 5 ]; then
echo " … and $((${#pointers[@]} - 5)) more" >&2
fi
exit 1
}

case "$SCOPE" in
all) add_scheme "$UNIT_SCHEME" ;;
snapshots) add_scheme "$SNAPSHOT_SCHEME" ;;
Expand Down Expand Up @@ -666,11 +712,16 @@ if [ "${#SCHEMES[@]}" -eq 0 ]; then
exit 1
fi

has_snapshot_scheme=false
for scheme in "${SCHEMES[@]}"; do
[ "$scheme" = "$SNAPSHOT_SCHEME" ] && has_snapshot_scheme=true
done

if [ "$has_snapshot_scheme" = true ]; then
require_hydrated_snapshot_references
fi

if [ -n "$TIMING_REPORT" ]; then
has_snapshot_scheme=false
for scheme in "${SCHEMES[@]}"; do
[ "$scheme" = "$SNAPSHOT_SCHEME" ] && has_snapshot_scheme=true
done
if [ "$has_snapshot_scheme" != true ]; then
echo "error: --timing-report requires a snapshot test scope" >&2
exit 1
Expand Down
Loading