Skip to content

fix(update): untrack the python/tetra3 symlink so git pull can update fielded units - #640

Merged
brickbots merged 1 commit into
mainfrom
worktree-tetra3-update-fix
Aug 28, 2026
Merged

fix(update): untrack the python/tetra3 symlink so git pull can update fielded units#640
brickbots merged 1 commit into
mainfrom
worktree-tetra3-update-fix

Conversation

@brickbots

Copy link
Copy Markdown
Owner

The failure

Units upgraded in place cannot update past 2.5.1, and every attempt reports success. Chain of events:

  1. migration_source/v2.1.0.sh created python/tetra3 on every updated unit as an absolute symlink via bare ln -s (on 2.2.2-era cards with a plain tetra3 folder already there, the link landed inside the folder instead).
  2. v2.6.0 (0a8262fa) started tracking the symlink (relative form) so CI could import tetra3.
  3. git pull refuses to write a tracked path over any untracked file — verified empirically, even a byte-identical symlink blocks it. So the pull to ≥ 2.6.0 aborts on every in-place-upgraded unit. Only fresh clones/images updated cleanly.
  4. pifinder_update.sh ignored the exit code and printed "PiFinder software update complete" anyway.

The trap: a stuck unit runs its old pifinder_update.sh, so no new pre-pull guard can reach it. The only new-release code it executes is pifinder_post_update.sh, sourced fresh from disk after a successful pull. The pull must therefore succeed first — which means git has to stop needing to write python/tetra3.

The fix

  • Untrack + gitignore python/tetra3. The release tree no longer touches the path, so whatever a unit has there can't block the pull.
  • pifinder_post_update.sh owns the symlink: anything at that path that isn't the relative link is moved aside to ~/tetra3_old_<timestamp> (kept, not deleted — same philosophy as the manual SSH fix), then the link is (re)created. Runs on every update, so it also self-heals. The ln -s in v2.1.0.sh (source of the absolute links) is removed.
  • Honest failure: pifinder_update.sh exits non-zero with a FAILED message on a failed pull; sys_utils.update_software() catches it and returns False, so the UI shows its (previously unreachable) "Error on Upd" instead of "Ok! Restarting".
  • The symlink is load-bearing (import tetra3 resolves the package through it; the sys.path append only exposes the package contents), so every tree-materialising path now creates it: pifinder_setup.sh, nox.yml, web-integration-tests.yml, and by hand in dev clones (CLAUDE.md updated).
  • ADR 0035 records the decision (0034 is reserved by fix(power): gate the gpio-poweroff overlay behind [cm4] so v3 poweroff completes #639).

Once this ships in a release, a stuck unit is rescued by one ordinary menu update — no SSH: the old script's pull succeeds against the new tree, it sources the new post-update from disk, and the repair block replaces the leftover folder/absolute link.

Verification

  • Sandbox git repos simulating all four fleet states running the old update script against a fixed release tree: stuck-with-absolute-symlink, stuck-with-plain-folder (2.2.2-era), healthy 2.6.x (tracked link), fresh clone — all pull cleanly, end with the correct relative link, leftovers moved aside.
  • New pifinder_update.sh with an unreachable remote: exits 1, prints FAILED, never prints "complete".
  • Setup-script repair block tested under set -e in all four states.
  • pytest -m smoke (5 passed), pytest -m unit (1526 passed), ruff check/format, mypy PiFinder/sys_utils.py clean.
  • Confirmed the symlink is required at runtime: without it, import tetra3 finds the inner tetra3.py module and collection fails — hence the setup/CI creation steps.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RbVpv53CQuxiNF9JHzDQtz

… fielded units

Tracking the symlink (v2.6.0, 0a8262f) made `git pull` refuse to update
any unit that already had an untracked python/tetra3 on disk: the
absolute symlink created by migration_source/v2.1.0.sh, or a plain
folder left over from the 2.2.2 era. git's refusal is path-level — even
a byte-identical symlink blocks the pull — so every unit upgraded in
place stalled at <= 2.5.1, while pifinder_update.sh ignored the exit
code and printed "update complete" anyway.

A stuck unit runs its old copy of pifinder_update.sh, so the fix has to
make the pull itself succeed; the only new code such a unit executes is
pifinder_post_update.sh, sourced fresh from disk after the pull.

- Untrack + gitignore python/tetra3. pifinder_setup.sh, CI (nox.yml,
  web-integration-tests.yml) and fresh clones create it explicitly;
  `import tetra3` resolves the solver package through it.
- pifinder_post_update.sh moves anything else at that path aside to
  ~/tetra3_old_<timestamp> (kept, not deleted) and (re)creates the
  relative link on every update. The `ln -s` in v2.1.0.sh — source of
  the absolute links — is removed.
- pifinder_update.sh exits non-zero on a failed pull and says FAILED;
  sys_utils.update_software() catches it and returns False, making the
  UI's existing "Error on Upd" branch reachable.
- ADR 0035 records the decision (0034 is reserved by PR #639).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RbVpv53CQuxiNF9JHzDQtz
@brickbots
brickbots marked this pull request as ready for review August 28, 2026 16:59
@brickbots
brickbots merged commit 9798571 into main Aug 28, 2026
4 checks passed
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.

1 participant