Repository: microsoft/terminal (language: cpp)

Issue:
Re-registered WSL distro profiles do not get auto generated on terminal restart unless state.json is deleted.
### Windows Terminal version (or Windows build number)

Microsoft Windows [Version 10.0.19043.1237]

### Other Software

WSL

### Steps to reproduce

- close terminal
- re-register an existing WSL distribution which had an existing profile in WT before
- remove that profile from WT's config
- open terminal
- config file does NOT contain the new distribution's auto-generated profile

### Expected Behavior

An auto-generated profile for the WSL distro which was registered.

### Actual Behavior

A profile is not added automatically, to make WT add the profile, the state.json file must be deleted, and the terminal must be reopened.

I'm just blindly guessing but I'm assuming this bug as to do w caching existing profiles in state.json, wt looks at the guid in state.json, assumes thats already generated, and skips trying to generate it again for the config.

Also, just for my own curiosity, how are the GUIDs generated for a distro? Is it a uuid v5 with the terminal uuid namespace? I registered a custom distro using `wsl --import` and it gave it a GUID too. A uuid v5 is what i could tell from the source code, but i tried generating some UUID v5s myself using the terminal's runtime namespace and the name of the distro, and the results weren't the same.


Discussion / hints:
Ok so I may have found the cause,

Before:

![image](https://user-images.githubusercontent.com/67145097/137368092-4cc0921b-e68b-4b8e-a0da-692718a4171c.png)


As you can see, `state.json` has the `Ubuntu` distro's GUID in the `generatedProfiles` array.

Now lets assume that the user decides to uninstall Ubuntu, and hence removes the Ubuntu profile from `settings.json`,

![image](https://user-images.githubusercontent.com/67145097/137368291-74b52954-5df6-469e-ac6e-ed7f16a546d2.png)


As you can see, the GUID of `Ubuntu` isn't removed from `state.json` upon removing it from `settings.json`.

Now lets assume that the user decides to reinstall Ubuntu again, and restarts Windows Terminal, since the GUID of Ubuntu is still in the generatedProfiles array even thought the profile IS NOT generated, the Ubuntu profile won't be regenerated this time, hence causing the bug.

The workaround has been to remove the GUID of that distro which is still stuck in state.json and make any modification to the config causing the state.json file to be rewritten.
Okay yep, that's exactly what happened here. Same as in #11593.

1. We found the WSL profile, generated it into the settings.json file, and then added that guid to `state.json` so we wouldn't generate it again.
2. We saw that the distro was gone, so we _removed it from the file_
3. When we re-found the profile, we see that we already added it into the `state`, so we skip it.

We should maybe not do 2 anymore. @lhecker thoughts?
Yes, we should not do 2. Only now after #11184 merged do we have the ability to _find_ disconnected generator "leaf profiles." They used to be omitted from the settings model entirely at parse time.

I've mentioned to a couple of you a UI that would display something like, "This profile was generated and seems to have disappeared. We've *kept your local copy* just in case." etc.
(we only do 2 when you [save] while the distro is gone. That's still a big footgun.)
Oh right I meant to test this today...
If I remove my WSL distro and launch WT the distro is _not_ removed from the file. However hitting "Save" in the settings UI removes it, as only "visible" profiles are saved.
Implementing Dustin's suggestion shouldn't be too hard. This is the code responsible for removing such profiles: https://github.com/microsoft/terminal/blob/9662bc69102e2e152a062a9060cb63d2742582d3/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp#L776-L788

I'm not sure if it would be enough to simply mark such profiles as "hidden".
So i had this problem just now when re-registering Debian, i notice that it didn't created the Debian profile even when i wanted to duplicate.

To resolve what i did was delete all `generatedProfiles` in `state.json` that wasn't in use in `settings.json`, then i go to duplicate a profile, and this time Debian was a option.

Fix patch:
diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.cpp b/src/cascadia/TerminalSettingsEditor/MainPage.cpp
index 739e0c5e6f8..62e496c1d15 100644
--- a/src/cascadia/TerminalSettingsEditor/MainPage.cpp
+++ b/src/cascadia/TerminalSettingsEditor/MainPage.cpp
@@ -457,6 +457,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
 
         _SetupProfileEventHandling(profile);
 
+        if (profile.Orphaned())
+        {
+            contentFrame().Navigate(xaml_typename<Editor::Profiles_Base_Orphaned>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
+            const auto crumb = winrt::make<Breadcrumb>(box_value(profile), profile.Name(), BreadcrumbSubPage::None);
+            _breadcrumbs.Append(crumb);
+            profile.CurrentPage(ProfileSubPage::Base);
+            return;
+        }
+
         contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
         const auto crumb = winrt::make<Breadcrumb>(box_value(profile), profile.Name(), BreadcrumbSubPage::None);
         _breadcrumbs.Append(crumb);
@@ -621,6 +630,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
         _Navigate(profileViewModel, BreadcrumbSubPage::None);
     }
 
+    static MUX::Controls::InfoBadge _createGlyphIconBadge(wil::zwstring_view glyph)
+    {
+        MUX::Controls::InfoBadge badge;
+        MUX::Controls::FontIconSource icon;
+        icon.FontFamily(winrt::Windows::UI::Xaml::Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
+        icon.FontSize(12);
+        icon.Glyph(glyph);
+        badge.IconSource(icon);
+        return badge;
+    }
+
     MUX::Controls::NavigationViewItem MainPage::_CreateProfileNavViewItem(const Editor::ProfileViewModel& profile)
     {
         MUX::Controls::NavigationViewItem profileNavItem;
@@ -628,6 +648,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
         profileNavItem.Tag(box_value<Editor::ProfileViewModel>(profile));
         profileNavItem.Icon(UI::IconPathConverter::IconWUX(profile.EvaluatedIcon()));
 
+        if (profile.Orphaned())
+        {
+            profileNavItem.InfoBadge(_createGlyphIconBadge(L"\xE7BA") /* Warning Triangle */);
+        }
+        else if (profile.Hidden())
+        {
+            profileNavItem.InfoBadge(_createGlyphIconBadge(L"\xED1A") /* Hide */);
+        }
+
         // Update the menu item when the icon/name changes
         auto weakMenuItem{ make_weak(profileNavItem) };
         profile.PropertyChanged([weakMenuItem](const auto&, const WUX::Data::PropertyChangedEventArgs& args) {
@@ -642,6 +671,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
                 {
                     menuItem.Content(box_value(tag.Name()));
                 }
+                else if (args.PropertyName() == L"Hidden")
+                {
+                    menuItem.InfoBadge(tag.Hidden() ? _createGlyphIconBadge(L"\xED1A") /* Hide */ : nullptr);
+                }
             }
         });
 
diff --git a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj
index 7d2aed1b744..7d6456886ac 100644
--- a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj
+++ b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj
@@ -113,6 +113,10 @@
       <DependentUpon>Profiles_Base.xaml</DependentUpon>
       <SubType>Code</SubType>
     </ClInclude>
+    <ClInclude Include="Profiles_Base_Orphaned.h">
+      <DependentUpon>Profiles_Base_Orphaned.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </ClInclude>
     <ClInclude Include="Profiles_Advanced.h">
       <DependentUpon>Profiles_Advanced.xaml</DependentUpon>
       <SubType>Code</SubType>
@@ -176,6 +180,9 @@
     <Page Include="Profiles_Base.xaml">
       <SubType>Designer</SubType>
     </Page>
+    <Page Include="Profiles_Base_Orphaned.xaml">
+      <SubType>Designer</SubType>
+    </Page>
     <Page Include="Profiles_Advanced.xaml">
       <SubType>Designer</SubType>
     </Page>
@@ -269,6 +276,10 @@
       <DependentUpon>Profiles_Base.xaml</DependentUpon>
       <SubType>Code</SubType>
     </ClCompile>
+    <ClCompile Include="Profiles_Base_Orphaned.cpp">
+      <DependentUpon>Profiles_Base_Orphaned.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </ClCompile>
     <ClCompile Include="Profiles_Advanced.cpp">
       <DependentUpon>Profiles_Advanced.xaml</DependentUpon>
       <SubType>Code</SubType>
@@ -354,6 +365,10 @@
       <DependentUpon>Profiles_Base.xaml</DependentUpon>
       <SubType>Code</SubType>
     </Midl>
+    <Midl Include="Profiles_Base_Orphaned.idl">
+      <DependentUpon>Profiles_Base_Orphaned.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Midl>
     <Midl Include="Profiles_Advanced.idl">
       <DependentUpon>Profiles_Advanced.xaml</DependentUpon>
       <SubType>Code</SubType>
diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp
index 66d8371c3ea..ab927bd890b 100644
--- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp
+++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp
@@ -225,6 +225,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
         return !IsBaseLayer();
     }
 
+    bool ProfileViewModel::Orphaned() const
+    {
+        return _profile.Orphaned();
+    }
+
     Editor::AppearanceViewModel ProfileViewModel::DefaultAppearance()
     {
         return _defaultAppearanceViewModel;
diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h
index c396fe38ec8..0b84a515ead 100644
--- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h
+++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h
@@ -85,6 +85,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
         bool AutoMarkPromptsAvailable() const noexcept;
         bool RepositionCursorWithMouseAvailable() const noexcept;
 
+        bool Orphaned() const;
+
         til::typed_event<Editor::ProfileViewModel, Editor::DeleteProfileEventArgs> DeleteProfileRequested;
 
         VIEW_MODEL_OBSERVABLE_PROPERTY(ProfileSubPage, CurrentPage);
diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl
index 9bf20079cba..08f0108a8a9 100644
--- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl
+++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl
@@ -81,6 +81,7 @@ namespace Microsoft.Terminal.Settings.Editor
         void CreateUnfocusedAppearance();
         void DeleteUnfocusedAppearance();
 
+	Boolean Orphaned { get; };
         OBSERVABLE_PROJECTED_PROFILE_SETTING(String, Name);
         PERMANENT_OBSERVABLE_PROJECTED_SETTING(Guid, Guid);
         OBSERVABLE_PROJECTED_PROFILE_SETTING(String, Source);
diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.cpp
new file mode 100644
index 00000000000..bc27cc4c2da
--- /dev/null
+++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#include "pch.h"
+#include "Profiles_Base_Orphaned.h"
+#include "Profiles_Base_Orphaned.g.cpp"
+#include "ProfileViewModel.h"
+
+#include <LibraryResources.h>
+#include "..\WinRTUtils\inc\Utils.h"
+
+using namespace winrt::Windows::UI::Xaml;
+using namespace winrt::Windows::UI::Xaml::Controls;
+using namespace winrt::Windows::UI::Xaml::Navigation;
+
+namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
+{
+    Profiles_Base_Orphaned::Profiles_Base_Orphaned()
+    {
+        InitializeComponent();
+        Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"Profile_DeleteButton/Text"));
+    }
+
+    void Profiles_Base_Orphaned::OnNavigatedTo(const NavigationEventArgs& e)
+    {
+        const auto args = e.Parameter().as<Editor::NavigateToProfileArgs>();
+        _Profile = args.Profile();
+
+        _layoutUpdatedRevoker = LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
+            // This event fires every time the layout changes, but it is always the last one to fire
+            // in any layout change chain. That gives us great flexibility in finding the right point
+            // at which to initialize our renderer (and our terminal).
+            // Any earlier than the last layout update and we may not know the terminal's starting size.
+
+            // Only let this succeed once.
+            _layoutUpdatedRevoker.revoke();
+
+            if (_Profile.FocusDeleteButton())
+            {
+                DeleteButton().Focus(FocusState::Programmatic);
+                _Profile.FocusDeleteButton(false);
+            }
+        });
+    }
+
+    void Profiles_Base_Orphaned::DeleteConfirmation_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*e*/)
+    {
+        winrt::get_self<ProfileViewModel>(_Profile)->DeleteProfile();
+    }
+}
diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.h b/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.h
new file mode 100644
index 00000000000..3fca070c89e
--- /dev/null
+++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.h
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#pragma once
+
+#include "Profiles_Base_Orphaned.g.h"
+#include "ViewModelHelpers.h"
+#include "Utils.h"
+
+namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
+{
+    struct Profiles_Base_Orphaned : public HasScrollViewer<Profiles_Base_Orphaned>, Profiles_Base_OrphanedT<Profiles_Base_Orphaned>
+    {
+    public:
+        Profiles_Base_Orphaned();
+
+        void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
+        void DeleteConfirmation_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
+
+        WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr);
+
+    private:
+        winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
+    };
+};
+
+namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
+{
+    BASIC_FACTORY(Profiles_Base_Orphaned);
+}
diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.idl b/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.idl
new file mode 100644
index 00000000000..e0266fd3d8a
--- /dev/null
+++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.idl
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+import "ProfileViewModel.idl";
+
+namespace Microsoft.Terminal.Settings.Editor
+{
+    [default_interface] runtimeclass Profiles_Base_Orphaned : Windows.UI.Xaml.Controls.Page
+    {
+        Profiles_Base_Orphaned();
+        ProfileViewModel Profile { get; };
+    }
+}
diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.xaml
new file mode 100644
index 00000000000..a21bbf5e85b
--- /dev/null
+++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base_Orphaned.xaml
@@ -0,0 +1,59 @@
+<!--
+    Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
+    the MIT License. See LICENSE in the project root for license information.
+-->
+<Page x:Class="Microsoft.Terminal.Settings.Editor.Profiles_Base_Orphaned"
+      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+      xmlns:local="using:Microsoft.Terminal.Settings.Editor"
+      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+      xmlns:model="using:Microsoft.Terminal.Settings.Model"
+      xmlns:mtu="using:Microsoft.Terminal.UI"
+      xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
+      mc:Ignorable="d">
+
+    <Page.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <ResourceDictionary Source="CommonResources.xaml" />
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+    </Page.Resources>
+
+    <StackPanel Style="{StaticResource SettingsStackStyle}">
+        <!--  Delete Button  -->
+        <local:SettingContainer x:Uid="Profile_Delete_Orphaned">
+            <local:SettingContainer.Content>
+                <Button x:Name="DeleteButton"
+                        Click="DeleteConfirmation_Click"
+                        Style="{StaticResource DeleteButtonStyle}">
+                    <Button.Content>
+                        <StackPanel Orientation="Horizontal">
+                            <FontIcon FontSize="{StaticResource StandardIconSize}"
+                                      Glyph="&#xE74D;" />
+                            <TextBlock x:Uid="Profile_DeleteButton"
+                                       Margin="10,0,0,0" />
+                        </StackPanel>
+                    </Button.Content>
+                </Button>
+            </local:SettingContainer.Content>
+        </local:SettingContainer>
+
+        <local:SettingContainer x:Uid="Profile_Name">
+            <local:SettingContainer.Content>
+                <TextBlock FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
+                           Style="{StaticResource SettingsPageItemDescriptionStyle}"
+                           Text="{x:Bind Profile.Name, Mode=OneTime}" />
+            </local:SettingContainer.Content>
+        </local:SettingContainer>
+
+        <local:SettingContainer x:Uid="Profile_Source_Orphaned">
+            <local:SettingContainer.Content>
+                <TextBlock FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
+                           Style="{StaticResource SettingsPageItemDescriptionStyle}"
+                           Text="{x:Bind Profile.Source, Mode=OneTime}" />
+            </local:SettingContainer.Content>
+        </local:SettingContainer>
+    </StackPanel>
+</Page>
diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
index 46524ca62d4..86c0ae46144 100644
--- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
@@ -1961,4 +1961,16 @@
     <value>Display a shield in the title bar when Windows Terminal is running as Administrator</value>
     <comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
   </data>
-</root>
\ No newline at end of file
+  <data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
+    <value>Profile no longer detected</value>
+  </data>
+  <data name="Profile_Delete_Orphaned.HelpText" xml:space="preserve">
+    <value>This automatically-detected profile appears to have been uninstalled. Changes you have made to it are preserved, but it cannot be used until it has been reinstalled.</value>
+  </data>
+  <data name="Profile_Source_Orphaned.Header" xml:space="preserve">
+    <value>Original Source</value>
+  </data>
+  <data name="Profile_Source_Orphaned.HelpText" xml:space="preserve">
+    <value>Indicates the software that originally created this profile.</value>
+  </data>
+</root>
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
index 6b412d28b68..eeb3d7cc2e3 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
@@ -103,7 +103,7 @@ Model::CascadiaSettings CascadiaSettings::Copy() const
             for (const auto& profile : targetProfiles)
             {
                 allProfiles.emplace_back(*profile);
-                if (!profile->Hidden())
+                if (!profile->Hidden() && !profile->Orphaned())
                 {
                     activeProfiles.emplace_back(*profile);
                 }
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
index 15a6bd153c9..ea9bda76166 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
@@ -1226,12 +1226,12 @@ CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) :
             const auto& parents = profile->Parents();
             if (std::none_of(parents.begin(), parents.end(), [&](const auto& parent) { return parent->Source() == source; }))
             {
-                continue;
+                profile->Orphaned(true);
             }
         }
 
         allProfiles.emplace_back(*profile);
-        if (!profile->Hidden())
+        if (!profile->Hidden() && !profile->Orphaned())
         {
             activeProfiles.emplace_back(*profile);
         }
diff --git a/src/cascadia/TerminalSettingsModel/Profile.cpp b/src/cascadia/TerminalSettingsModel/Profile.cpp
index 561a21017fc..88ae6e8590b 100644
--- a/src/cascadia/TerminalSettingsModel/Profile.cpp
+++ b/src/cascadia/TerminalSettingsModel/Profile.cpp
@@ -104,6 +104,7 @@ winrt::com_ptr<Profile> Profile::CopySettings() const
     const auto defaultAppearance = AppearanceConfig::CopyAppearance(winrt::get_self<AppearanceConfig>(_DefaultAppearance), weakProfile);
 
     profile->_Deleted = _Deleted;
+    profile->_Orphaned = _Orphaned;
     profile->_Updates = _Updates;
     profile->_Guid = _Guid;
     profile->_Name = _Name;
diff --git a/src/cascadia/TerminalSettingsModel/Profile.h b/src/cascadia/TerminalSettingsModel/Profile.h
index 24fbb7d1f2d..86110c206ea 100644
--- a/src/cascadia/TerminalSettingsModel/Profile.h
+++ b/src/cascadia/TerminalSettingsModel/Profile.h
@@ -115,6 +115,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         void Icon(const hstring& value);
 
         WINRT_PROPERTY(bool, Deleted, false);
+        WINRT_PROPERTY(bool, Orphaned, false);
         WINRT_PROPERTY(OriginTag, Origin, OriginTag::None);
         WINRT_PROPERTY(guid, Updates);
 
diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl
index b93a52aa797..331fc1345cf 100644
--- a/src/cascadia/TerminalSettingsModel/Profile.idl
+++ b/src/cascadia/TerminalSettingsModel/Profile.idl
@@ -41,6 +41,8 @@ namespace Microsoft.Terminal.Settings.Model
 
         // True if the user explicitly removed this Profile from settings.json.
         Boolean Deleted { get; };
+        // True if the user *kept* this Profile, but it disappeared from the system.
+        Boolean Orphaned { get; };
 
         // Helper for magically using a commandline for an icon for a profile
         // without an explicit icon.


Test patch: