Skip to content

fix(shell): the loader written into ~/.profile could never be removed, by anything - #14

Open
fcreme wants to merge 1 commit into
mainfrom
fix/profile-loader-unremovable
Open

fix(shell): the loader written into ~/.profile could never be removed, by anything#14
fcreme wants to merge 1 commit into
mainfrom
fix/profile-loader-unremovable

Conversation

@fcreme

@fcreme fcreme commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Found by an adversarial audit — two independent lenses reported it, 0 of 3 verifiers refuted, and I reproduced it before fixing.

The bug

shell-init registers into ~/.profile in two branches, because in that layout it is the only file the login shell will read and creating a .bash_profile would shadow a file already in use.

~/.profile is deliberately not a registration candidate — it is sh's file rather than bash's, and writing to it unasked would reach past the shells this tool claims. Removal walked that same registration list, so it never looked at the file init had just written to:

shell-init   -> added ~/.bashrc, added ~/.profile
shell-remove -> "removed from ~/.bashrc"
                "Open a new tab to get your original prompt back."
left in ~/.profile: 1

The message is false in the way that matters: ~/.profile is the file the login shell reads, so the loader the user was told was gone is the only one still running. After tstyles uninstall it points at a data root that has just been deleted — on every login shell, permanently, with nothing left on the machine able to remove it and nothing that ever said it was there.

The fix

Removal walks Get-ShellRcRemovalCandidate: the registration list plus ~/.profile. Write the narrow set, sweep the wide one. Unregister-ShellLoader returns 'none' for a file with no block and never creates a file that does not exist, so the extra entry is free wherever it was never used.

The test that should have caught it

There was one. It asserted that shell-init and uninstall both mention Get-ShellRcCandidate:

$init      | Should -Match 'Get-ShellRcCandidate'
$uninstall | Should -Match 'Get-ShellRcCandidate'

Sharing a helper name is not symmetry, and that lint passed for the entire life of the bug. Replaced with a behavioural round-trip over six rc layouts — run init, run remove, count the markers left on disk — plus a separate assertion that the user's own lines came back. Mutation-checked: reverting the fix turns the ~/.profile-only and .bashrc + ~/.profile layouts red, naming the file left behind.

Provenance

This predates the ~/.profile registration I added for #9 in 0.8.21. I verified that: 51f401e already had one $dotProfile registration — the .bashrc-exists branch — and it orphans identically. My change added a second route into an existing hole, and I should have tested the inverse when I made it. The round-trip covers both branches.

Verification

1435 passed, 0 failed. Shell tests write to real rc paths, so I checksummed ~/.zshrc, ~/.bashrc, ~/.bash_profile and ~/.profile before and after — unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Fc4mE7gSHoux5wL6VEo59S

…, by anything

shell-init registers into ~/.profile in two branches -- the one where
.bashrc exists alongside it, and the nothing-existed fallback -- because
in that layout it is the only file the login shell will read, and
creating a .bash_profile would shadow a file already in use.

~/.profile is deliberately NOT a registration candidate: it is sh's file
rather than bash's, and writing to it unasked would reach past the shells
this tool claims. Removal walked that same registration list, so it never
looked at the file init had just written to.

  shell-init   -> added ~/.bashrc, added ~/.profile
  shell-remove -> "removed from ~/.bashrc"
                  "Open a new tab to get your original prompt back."
  left in ~/.profile: 1

The message is false in the way that matters: ~/.profile is the file the
login shell reads, so the loader the user was told was gone is the only
one still running. After `tstyles uninstall` it points at a data root
that has just been deleted -- on every login shell, permanently, with
nothing left on the machine able to remove it and nothing that ever said
it was there.

Removal now walks Get-ShellRcRemovalCandidate: the registration list plus
~/.profile. Write the narrow set, sweep the wide one. Unregister-ShellLoader
returns 'none' for a file with no block and never creates a file that does
not exist, so the extra entry is free wherever it was never used.

The test meant to guarantee this asserted that shell-init and uninstall
both MENTION Get-ShellRcCandidate. Sharing a helper name is not symmetry,
and that lint passed for the entire life of the bug. Replaced with a
behavioural round-trip over six rc layouts: run init, run remove, count
the markers left on disk, and separately assert the user's own lines came
back. Checked by reverting the fix -- the ~/.profile-only and
.bashrc+~/.profile layouts both go red, naming the file left behind.

Found by an adversarial audit (3 independent verifiers, 0 refutations).
Note this predates the ~/.profile registration added for #9 in 0.8.21:
the .bashrc-exists branch has orphaned the same way since it was written,
and the round-trip covers both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fc4mE7gSHoux5wL6VEo59S
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