# nobrainer-tech-flow 2.4.1: undo after a rearranged profile

Release date: 2026-10-19.

Two fixes to the setup record from an independent review of the 2.3.2 installer
(issue #82). In both cases the undo used to stop with exit 3 and change nothing, so no
data was lost, but the skill links stayed installed until the record was fixed by hand.

## What changes

- **Undo works after a link joins two folders the setup made.** After a Codex setup on
  a fresh profile, moving `~/.agents/skills` to `~/.codex/skills` and replacing
  `~/.agents` with a link to `~/.codex` makes two entries of `created_dirs` lead to
  one folder. `install.sh`, `install.py` and the guided `--rollback` refused that record
  as listing a folder twice. They now look for a repeated entry by its spelling in the
  record, so a link added after the setup no longer counts as a repeat. The undo
  removes the links, leaves the `~/.agents` link and everything in `~/.codex` as they
  are, and prints a `PRESERVED` line for the folder that now lies behind the link. A
  record that lists the same path twice is still refused with exit 3. When two
  different entries lead to one folder, the record cannot show which of them the setup
  made, so the undo removes neither: each one stays with a `PRESERVED` line, while the
  links and the other recorded folders are still removed.
- **A folder whose real path holds a line break stops the setup before it writes.**
  The record lists each folder the setup makes with the links on the way resolved.
  When a link on the way leads to a folder whose name holds a line break or another
  control character (for example `~/.claude` linked to such a folder), `install.sh`
  wrote that path into the record split over two lines, so apply exited 0 and the
  undo later refused the record. The Python installer wrote such a path for a folder
  behind a link that is not on the skills path (for example `CODEX_HOME` set to a
  folder still to be made behind such a link). Both installers now refuse the apply
  with exit 2 and an error that names the folder, before anything is written, as they
  already did for a path written with such a character. `install.sh` also keeps a
  line break at the end of a folder name when it resolves links; it used to drop it.

## Verification record

Local checks (macOS, Python 3.14.5):

- Two new tests in `tests.test_shell_installer` run each case with `install.sh` under
  `/bin/sh`, dash, bash in POSIX mode and zsh emulating sh, and with `install.py`.
  Against the 2.4.0 scripts they failed in 23 subtests: every `install.sh` run of both
  cases, the `install.py` undo of the rearranged profile, and the `install.py` apply
  with `CODEX_HOME` behind a link. The `install.py` apply with `~/.claude` linked to
  such a folder was already refused. Without the trailing line break fix, 8 `install.sh`
  subtests for a folder name that ends in a line break still failed.
- The case "a folder listed under two spellings" was removed from the test of damaged
  records, because such a record is now accepted; the case of a folder listed twice
  stays.
- A third test, from a review of this fix, lists an existing empty home under its real
  path and through a link to it. The undo exits 0 and the home stays in all 5 runs; with
  the first version of this fix, the undo removed the home in all 5.
- `python3 -m unittest tests.test_shell_installer tests.test_one_command_installer
  tests.test_guided_onboarding tests.test_installer tests.test_install_guards`: 205
  tests passed, 2 skipped.
- `sh -n` and `dash -n` on `scripts/install.sh`, `shellcheck -S error` and
  `git diff --check` passed.

The line break case is not tested on Windows, which does not allow such a file name.

## Scope

This is an installer fix. The record format, the module set and the instruction
block do not change.
