Skip to content

Prevent self-upgrades from nesting the managed payload - #99

Merged
woud420 merged 1 commit into
masterfrom
codex/eng-204-stable-upgrade-layout
Jul 20, 2026
Merged

Prevent self-upgrades from nesting the managed payload#99
woud420 merged 1 commit into
masterfrom
codex/eng-204-stable-upgrade-layout

Conversation

@woud420

@woud420 woud420 commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Primary changes

  • Preserve the logical kickstart invocation path so self-upgrade can recover the public launcher directory and configured app root without resolving away the launcher symlink.
  • Collapse the managed payload nesting produced by older upgrades, then keep the single <app_root>/current/kickstart layout stable across subsequent upgrades.
  • Stage new onedir bundles before activation and roll back ordinary staging or activation errors instead of deleting the active payload first.
  • Document the stable repair behavior in the binary-installation operations runbook.

Reviewer walkthrough

  1. Start in src/utils/installer.py: current_entrypoint_path() preserves logical invocation spelling while current_binary_path() remains the resolved install source; _replace_bundle() stages and rolls back replacement.
  2. Review src/utils/updater.py: layout discovery reads only the launcher's first symlink hop and recognizes wrapper-launched managed payloads without changing legacy single-file handling.
  3. Review tests/unit/utils/test_updater.py: the production helper composition covers absolute and relative launchers, starts from an already-nested layout, and performs two complete upgrades against a real release archive.
  4. Review tests/unit/utils/test_installer.py: entrypoint spelling, staging failure, and activation rollback cases protect the repaired layout.
  5. Confirm docs/operations/install-binaries.md states the same stable-root and repair contract.

Correctness and invariants

  • The public launcher remains outside the replaceable payload and continues to identify the original app root through its immediate target, even when an older payload contains deeper symlink layers.
  • Repeated successful upgrades converge on one current payload and remove the accidental nested tree.
  • Wrapper-launched managed payloads refresh their stable bundle in place; regular legacy single-file entrypoints still return no managed app root.
  • Upgrade messages, exit behavior, checksum verification, UpgradeResult, and telemetry outcome categories are unchanged.
  • Staging and activation exceptions preserve or restore the previous payload. As with the existing directory-based layout, an abrupt process or machine termination during the short directory-swap window is not crash-atomic.

Testing and QA

  • Regression-first evidence: three focused updater tests failed before the fix, showing the launcher directory resolving inside the payload and a second nested .kickstart/current layer.
  • CI=true make check — passed Ruff, mypy, type/import/template audits, and all 792 tests.
  • poetry run ruff format --check src/utils/installer.py src/utils/updater.py tests/unit/utils/test_installer.py tests/unit/utils/test_updater.py — passed.
  • git diff --check — passed.
  • Post-restack topology — exactly one commit ahead of origin/master (0 1), with 705e8e9 (the squash merge of PR Add default-on CLI telemetry and lifecycle events #97) as the commit's direct parent.
  • Tree-equivalence check — the rebased commit has no content diff from pre-restack commit 0dbaf43; the rewrite changed ancestry only.
  • GitHub Actions post-restack CI run 29709258020 — all 10 executed checks passed on rebased commit 3b28339 across coverage, lint/typecheck, Python 3.12–3.14 on Linux and macOS, website, and integration/eval smoke tests; the manual package job was skipped by design.

Risk / Rollout

  • PR Add default-on CLI telemetry and lifecycle events #97 was squash-merged into master; this PR is now restacked directly on that squash commit and exposes only the ENG-204 change.
  • The first successful fixed upgrade repairs the previously observed nested managed layout automatically; no separate repair command or user action is required.
  • The restack rewrote commit ancestry without changing the five-file patch.
  • Release execution remains out of scope for this PR.

Resolves ENG-204

@woud420
woud420 force-pushed the codex/eng-204-stable-upgrade-layout branch from 7d6277c to 0dbaf43 Compare July 20, 2026 00:02
@woud420
woud420 marked this pull request as ready for review July 20, 2026 00:09
Preserve the logical launcher path, repair existing nested onedir payloads, and stage bundle swaps with rollback.

Add repeated-upgrade and failure regressions and document the stable repair behavior.

Refs ENG-204
@woud420
woud420 force-pushed the codex/eng-204-stable-upgrade-layout branch from 0dbaf43 to 3b28339 Compare July 20, 2026 00:16
@woud420
woud420 changed the base branch from codex/eng-200-create-telemetry to master July 20, 2026 00:16
@woud420
woud420 merged commit 5e7df33 into master Jul 20, 2026
11 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