Skip to content

fix(reset): reset deleted profile settings the user set by hand, and reported success - #15

Merged
fcreme merged 2 commits into
mainfrom
fix/reset-deletes-hand-set-fields
Sep 4, 2026
Merged

fix(reset): reset deleted profile settings the user set by hand, and reported success#15
fcreme merged 2 commits into
mainfrom
fix/reset-deletes-hand-set-fields

Conversation

@fcreme

@fcreme fcreme commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Found by an adversarial audit (0 of 3 verifiers refuted), then reproduced against the real module before fixing.

The bug

Reset-StyleDirect stripped every name in $script:TStylesThemeFields present on the target profile, with no record of which of them TerminalStyles actually wrote:

foreach ($field in $script:TStylesThemeFields) {
    if ($entry.PSObject.Properties.Match($field).Count) {
        $entry.PSObject.Properties.Remove($field)
    }
}

That list is colorScheme, tabTitle, tabColor, cursorShape, useAcrylic, opacity, experimental.retroTerminalEffect, font, padding plus the four backgroundImage* keys — very nearly everything a user configures through the Windows Terminal settings UI.

Failure scenario. A user sets acrylic, opacity 60, padding, a bar cursor, Consolas 14, their own wallpaper and a tab title on their PowerShell profile. They never apply a style to it. They run tstyles reset -Target 'PowerShell' out of curiosity. All eight keys are deleted, and the command prints Reset 'PowerShell' to its unstyled default. in green. Only name, guid and unlisted keys survive.

README.md:222 has promised the opposite since it was written:

settings.json.bak first. Fields you set on the profile by hand are left alone.

The asymmetry is the tell: the apply path already refuses the symmetric thing. Test-ManagedBackgroundPath exists precisely so a background the user set is not cleared by a style that ships none — the 'skip' arm in Merge-StyleIntoSettings is this exact question, asked. Reset never asked it.

The fix

Reset now requires the profile to carry a colorScheme naming a style this tool knows. An apply always writes that key and writes it last, so there is no way to be styled by TerminalStyles and not carry it. A profile without one is one we never touched — the honest answer is to change nothing and say so, rather than guess at provenance.

A second defect on the same path, fixed by ordering

The check runs before Save-SettingsBackup. A reset that changed nothing previously still:

  • rolled settings.json.bak over, spending the user's only undo of their last real apply; and
  • rewrote settings.json, re-serialising the parsed object and dropping every JSONC comment in it.

The likeliest way a curious user tried this command was also the way that cost them their comments.

Tests

The existing test asserted only that schemes[] survived a reset of a profile with no colorScheme — so the field destruction on that exact fixture went unnoticed. Now:

  • nothing is written at all for a profile with no colorScheme;
  • hand-set fields survive byte-for-byte on disk (useAcrylic, wallpaper.png, cursorShape all still present);
  • the other direction — a profile whose colorScheme names a real style still resets fully, so the guard cannot quietly disable the feature.

Mutation-checked: reverting the guard turns the first two red.

Verification

1428 passed, 0 failed.

Scope note

This is Windows-only in effect (there is no settings.json off Windows Terminal), which is why it survived — the project is developed on a Mac.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Fc4mE7gSHoux5wL6VEo59S

fcreme and others added 2 commits September 3, 2026 21:01
…reported success

Reset-StyleDirect stripped every name in $script:TStylesThemeFields that
was present on the target profile, with no record of which of them
TerminalStyles had actually written. That list is colorScheme, tabTitle,
tabColor, cursorShape, useAcrylic, opacity,
experimental.retroTerminalEffect, font, padding and the four
backgroundImage keys -- very nearly everything a user configures through
the Windows Terminal settings UI.

So: a user sets acrylic, opacity 60, padding, a bar cursor, Consolas 14,
their own wallpaper and a tab title on their PowerShell profile. They
never apply a style to it. They run `tstyles reset -Target 'PowerShell'`
out of curiosity. All eight keys are deleted and the command prints
"Reset 'PowerShell' to its unstyled default." in green. README.md:222
has promised the opposite since it was written: "Fields you set on the
profile by hand are left alone."

The apply path already refuses the symmetric thing.
Test-ManagedBackgroundPath exists so that a background the USER set is
not cleared by a style that ships none -- the 'skip' arm in
Merge-StyleIntoSettings is exactly this question, asked. Reset never
asked it.

Reset now requires the profile to carry a colorScheme naming a style this
tool knows. An apply always writes that key, so there is no way to be
styled by TerminalStyles and not carry it; a profile without one is one
we never touched, and the honest answer is to change nothing and say so
rather than guess at provenance.

The check runs BEFORE Save-SettingsBackup, which fixes a second defect on
the same path: a reset that changed nothing still rolled settings.json.bak
over -- spending the user's only undo of their last real apply -- and
still rewrote settings.json, re-serialising the parsed object and
dropping every JSONC comment in it. The likeliest way a curious user
tried this command was also the way that cost them their comments.

The existing test asserted only that schemes[] survived a reset of a
profile with no colorScheme, so the field destruction on that exact
fixture went unnoticed. It now asserts nothing is written at all, plus a
case that hand-set fields survive byte-for-byte on disk, plus the other
direction -- a profile whose colorScheme names a real style still resets
fully, so the guard cannot quietly disable the feature. Checked by
reverting the guard: both new tests go red.

Found by an adversarial audit (3 independent verifiers, 0 refutations),
then reproduced against the real module before fixing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fc4mE7gSHoux5wL6VEo59S
@fcreme
fcreme merged commit 22d32cc into main Sep 4, 2026
8 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