# Осиротевшие процессы MCP могут оставить работу после себя

Сбой клиента не говорит, остановился ли его MCP-сервер. Он сообщает лишь, что один процесс завершился или, по крайней мере, перестал отвечать. Разница важна: stdio это транспорт, а не выключатель для работы, которая уже перешла границу процесса.

Я видел, как команды считали скопление старых вспомогательных процессов обычной проблемой уборки, а потом обнаруживали, что один из них всё ещё хранил облачный токен, управляющий SSH-сокет или очередь задач, не связанную ни с одним неудачным запуском. Команду очистки выполнить было легко. Сложнее всего оказалось восстановить, кто авторизовал последнее действие, потому что об этом никто не позаботился.

Правильная реакция состоит из трёх отдельных задач: точно определить оставшийся процесс, доказать, сохранился ли у него путь к выполнению действий, и сохранить запись, связывающую его последние вызовы с владевшей ими сессией. Выполняйте эти задачи именно в таком порядке. Если начать с завершения процесса, помеха исчезнет, но вместе с ней можно потерять лучшие доказательства произошедшего.

## Процесс без родителя это подсказка, а не приговор

Осиротевший процесс это дочерний процесс, исходный родитель которого завершился, после чего операционная система назначила ему нового родителя, часто с PID 1. Это полезное свидетельство, но не определение небезопасного MCP-сервера. Менеджеры процессов, оболочки, IDE и службы запуска могут переподчинять исправные дочерние процессы во время обычной работы.

Для MCP-сервера stdio задайте более узкий вопрос: принадлежит ли процесс ещё живому клиентскому соединению или продолжает работать после исчезновения клиента, которому принадлежали его stdin и stdout? Ответ требует одновременно проверить родословную процессов, открытые дескрипторы, прошедшее время и журнал активности.

Документация Model Context Protocol описывает stdio как локальную интеграцию, запускающую отдельный процесс. Клиент запускает команду и обменивается с сервером JSON-RPC, разделённым переводами строк, через стандартные ввод и вывод. Для корректно написанного сервера это даёт простой сигнал завершения: когда на входе появляется EOF, клиентская сторона исчезла.

EOF свидетельствует о сломанном или закрытом транспорте. Он не доказывает, что остановились все рабочие процессы, дочерние процессы, сетевые соединения, таймеры и удалённые задачи. Сервер может получить вызов инструмента, начать работу, а затем потерять клиента до отправки ответа JSON-RPC. Если обработчик запустил подпроцесс или отправил запрос во внешнюю систему, такая работа может жить по собственным правилам.

Здесь часто смешивают два разных сбоя:

- **Осиротевший процесс операционной системы** потерял исходного родителя или отсоединился от ожидаемого дерева процессов.
- **Логически осиротевший процесс** потерял сессию, которая придавала его работе смысл, даже если его PPID всё ещё выглядит нормально.

Первый может расходовать память или удерживать порт. Второй может внести нежелательное изменение во внешней системе. Проверять нужно оба случая.

Процесс с PPID 1, большим временем работы и без открытого stdin подозрителен. Опасным может быть и процесс, всё ещё подключённый к активному терминалу, если его клиент-владелец незаметно завис, а у процесса есть учётные данные или повторно используемое соединение. И наоборот, процесс, оставшийся после сбоя, может быть безвредным, если у него нет полномочий и доступа к каналам действий.

Не создавайте правило «PPID 1 означает, что процесс нужно завершить». Создайте запись, в которой указано, зачем существует этот процесс, какой запуск его создал, к чему он всё ещё имеет доступ и что вы с ним сделали.

## Начните с инвентаризации процессов, которую можно обосновать

Снимите состояние до отправки любого сигнала. После завершения процесса нельзя восстановить командную строку, группу процессов или открытый дескриптор, а повторное использование PID быстро превращает небрежные заметки в догадки.

В macOS начните с полного списка, а не с хитрой однострочной команды, которая может отфильтровать нужные доказательства:

```sh
ps -axo user,pid,ppid,pgid,stat,etime,command
```

Ищите команды, которые запускает ваш MCP-клиент. Это может быть непосредственный бинарный файл сервера, интерпретатор вроде `node` или `python`, запускатель пакетов, SSH-помощник или shim `sp mcp`. Скопируйте подходящие строки в заметку об инциденте до того, как сузите поиск.

Поля отвечают на разные вопросы:

- `pid` идентифицирует процесс только в рамках этой проверки.
- `ppid` показывает, существует ли его непосредственный родитель.
- `pgid` обозначает группу процессов и часто помогает найти соседние помощники, запущенные вместе.
- `stat` может показать, спит ли процесс, остановлен ли он или не может прервать операцию ввода-вывода.
- `etime` показывает, не работает ли якобы недавний запуск уже несколько часов или дней.
- `command` часто остаётся единственной записью о файле и аргументах, с которыми был запущен процесс.

Затем проверьте каждый кандидат отдельно. Подставьте фактический PID и сохраните вывод с отметкой времени в записи об инциденте.

```sh
ps -o pid=,ppid=,pgid=,stat=,etime=,user=,command= -p 48271
lsof -nP -p 48271
```

Первая команда даёт компактную запись об идентичности процесса. Вторая показывает, что он всё ещё держит открытым. Обратите особое внимание на файловые дескрипторы 0, 1 и 2. У обычного stdio-сервера обычно есть дескриптор чтения stdin и дескрипторы записи stdout и stderr. Если stdin достиг EOF, процесс всё ещё может отображать дескриптор канала, поэтому нельзя делать вывод о работоспособности только по его наличию. Нужна более полная картина: соседний процесс, терминалы, обычные файлы, Unix-сокеты, TCP-соединения и дочерние рабочие процессы.

`lsof` может показать ошибку, которую скрывает список процессов. Допустим, родитель сервера исчез, но сам сервер всё ещё владеет установленным TCP-соединением с внутренним API. Это не доказывает, что он может отправить новый запрос, но даёт конкретную возможность для проверки. Если процесс владеет управляющим SSH-сокетом или локальным Unix-сокетом, подключённым к помощнику учётных данных, отнеситесь к этому как к направлению расследования, а не как к основанию считать процесс безопасным.

Перед действием проверьте группу процессов:

```sh
ps -axo pid,ppid,pgid,stat,etime,command | awk '$3 == 48271 || $2 == 48271'
```

В этом примере предполагается, что `48271` это наблюдавшийся идентификатор группы. Замените его фактическим значением. Вывод может показать оболочку-обёртку, сервер и дочерний помощник, которых простая команда `kill` для одного PID оставила бы работать. Он также может подтвердить, что у процесса больше нет соседей и его проще изолировать.

Избегайте распространённого сокращённого пути: не ищите только `node`, `python` или `npx` и не завершайте каждый найденный процесс. Эти названия обозначают среды выполнения, а не конкретные процессы. Массовая очистка может сломать расширение редактора, локальный тест, задачу сборки или несвязанную автоматизацию. Сопоставьте полную командную строку с ожидаемой конфигурацией сервера, затем проверьте его родителя и группу.

Если клиент записывает команду сервера при запуске, храните её рядом с записью сессии. Тогда последующее расследование превратится из расплывчатого поиска по имени процесса в прямое сравнение.

## Канал stdio не определяет границу действия

Корректно написанный сервер stdio должен прекращать приём запросов после закрытия stdin. Он должен отменить ещё не начатую работу, закрыть транспорт и завершиться после ограниченной очистки. В руководстве официального TypeScript MCP SDK в разделе о завершении сказано то же самое: закрытие транспорта автоматически не дожидается завершения активных обработчиков инструментов перед выходом процесса.

Последнему моменту уделяют меньше внимания, чем он заслуживает. Когда клиент падает, обработчик инструмента может находиться в одном из нескольких состояний:

1. Внешняя работа ещё не началась, и её можно безопасно отменить.
2. Запрос отправлен, но ответ ещё не получен.
3. Удалённое изменение уже выполнено, но путь к ответу потерян до сообщения об успехе.
4. Запущен локальный дочерний процесс или удалённая задача, которые переживут обработчик.
5. Обработчик заблокирован в ожидании внешней зависимости и может продолжить работу позже.

Только первое состояние можно безопасно описать словами «ничего не произошло». Для остальных четырёх нужны записи за пределами погибшего клиентского процесса.

Рассмотрим конкретный пример. Сервер получает вызов инструмента для развёртывания сборки. Он отправляет запрос в удалённый API сборки, после чего клиент падает, пока API обрабатывает запрос. Сервер видит разорванный канал stdout. Если он немедленно завершится, сборка всё равно может продолжиться. Если он повторит запрос без механизма идемпотентности, может начаться вторая сборка. Если он останется работать с долгоживущими учётными данными, то после исчезновения исходного клиента может продолжить опрос, повторные попытки или запуск следующей работы.

Проблема не в том, что сервер остался работать. Проблема в том, что закрытие транспорта приняли за полное описание авторизации, отмены и состояния удалённой системы.

Для каждого канала действий явно определите границу:

- Какое событие прекращает поступление новой работы на сервер?
- Какой идентификатор позволяет запросить состояние или отменить уже отправленную работу?
- Поддерживает ли удалённая операция идемпотентность или токен запроса?
- Какой процесс всё ещё хранит учётные данные после исчезновения клиента?
- Где записывается итог, если ответ JSON-RPC невозможно доставить?

Если для инструмента с высоким уровнем воздействия нет ответов на эти вопросы, не называйте его безопасным только потому, что он использует stdio.

То же правило относится к SSH. Команда, похожая на интерактивную, могла запустить фоновый процесс на удалённом хосте. Закрытие локального клиента может закрыть локальный канал, пока отсоединившаяся от оболочки удалённая команда продолжает работу. Серверу нужен дизайн команд, делающий удалённую работу наблюдаемой и отменяемой, а не надежда на отключение терминала.

## Докажите, может ли оставшийся процесс продолжать действовать

Сам факт существования процесса не означает наличия полномочий. Нужно проверить путь к действию, не вызывая настоящее действие.

Сначала определите, где находятся полномочия. Сервер, читающий API-токен из собственного окружения, несёт другой риск, чем сервер, который для каждой операции обращается к отдельному брокеру. Сервер с приватным SSH-ключом в файле отличается от сервера, работающего через локальный помощник с короткоживущими правами. Запишите ответ для конкретного проверяемого сервера.

Затем проверьте, что процесс всё ещё держит открытым и к чему может обратиться. `lsof` даёт отправную точку, но не показывает каждый секрет в памяти и каждую аутентифицированную сессию. Сопоставьте его вывод со своими журналами операций и, если возможно, с аудитом внешней системы.

Полезная последовательность расследования выглядит так:

1. Запишите PID кандидата, команду, PPID, группу процессов и открытые сетевые или Unix-сокеты.
2. Найдите последнее внешнее действие, связанное с этим процессом или его сессией.
3. Проверьте, происходило ли новое действие после времени сбоя клиента.
4. Отзовите или отключите путь авторизации процесса.
5. Проследите, пытается ли процесс выполнить ещё одно действие и отклоняет ли его шлюз.

Не проверяйте это, прося осиротевший сервер выполнить запись, которая кажется безвредной. Во многих системах по-настоящему безвредных записей не бывает, а тестовый вызов может изменить лимиты, создать лишний шум в аудите, обновить состояние или запустить автоматизацию. Лучше использовать доступную операцию чтения, например безопасный эндпоинт проверки состояния или идентичности. Ещё лучше подтвердить отказ через шлюз или брокер учётных данных после отзыва прав.

Важно различать две вещи: процесс с доступом к сети не обязательно может выполнить действие, а процесс без видимого сетевого соединения всё ещё может получить такую возможность позже. Путь могут снова открыть разрешение DNS, прокси, локальный помощник, отложенный таймер или дочерний процесс. Поэтому проверка отзыва прав важнее снимка сокетов.

Если сервер напрямую хранит повторно используемый секрет, очистка должна включать его ротацию или отзыв. Завершение процесса удаляет одну копию из памяти, но не меняет того, что злоумышленник, дамп памяти или удалённый сервис могут сделать с тем же секретом. Команды часто избегают ротации, потому что она создаёт дополнительную работу. Это понятно, но не служит аргументом безопасности.

Модель со шлюзом меняет ход расследования. Сервер может оставаться недоверенным процессом, но не сможет выполнить новый защищённый вызов, если у него никогда не было секрета и больше нет одобренной активной сессии. Sallyport хранит API- и SSH-секреты в зашифрованном хранилище и сам выполняет операции, поэтому агент не получает секрет ни в открытом виде, ни в форме заменяющего значения. Это уменьшает ущерб от случайного процесса stdio, но подозрительный запуск всё равно нужно отозвать, а не считать, что сбой сделал это за вас.

Практический стандарт прост: после отзыва сессии или пути к действию защищённый вызов должен быть отклонён, а отказ записан. Если вы не можете это продемонстрировать, containment не подтверждён.

## Запишите последние вызовы до того, как очистка изменит картину

Запись об инциденте должна опираться на стабильные идентификаторы, а не на историю, восстановленную по памяти. PID временный и может быть переиспользован. Командная строка может измениться после обновления. Идентификатор сессии и защищённая от подмены запись аудита дают гораздо более надёжную опору.

Как минимум создайте одну строку для каждого подозрительного процесса со следующими полями:

```text
Наблюдалось в:
PID и команда клиентского процесса:
PID и команда MCP-сервера:
Родительский PID и группа процессов:
Идентификатор сессии:
Состояние авторизации:
Время последнего успешного действия:
Время последней попытки действия:
Цель и операция:
Результат или идентификатор удалённой задачи:
Время отзыва и оператор:
Сигнал завершения и результат выхода:
Требуется продолжение:
```

Самая важная пара это идентификатор сессии и последняя попытка действия. Многие команды записывают только успешные вызовы, скрывая самый показательный момент расследования сбоя: запрос, который покинул локальную машину, но не получил результата.

Разделяйте три временные отметки. Записывайте, когда клиент стал недоступен, когда началось последнее известное действие сервера и когда были отозваны права. Если использовать одно общее «время инцидента», невозможно понять, произошёл ли вызов до сбоя, в неопределённом промежутке или после того, как containment уже должен был вступить в силу.

Сохраняйте также цель действия. Записи «вызван облачный API» недостаточно. Укажите аккаунт или класс эндпоинта, метод или категорию SSH-команды, а также идентификатор запроса или задачи, по которому оператор сможет найти операцию во внешнем сервисе. Не храните чувствительные тела запросов в общей заметке об инциденте. Нужно достаточно сведений для сверки побочного эффекта, а не вторая копия всех секретов и записей клиентов.

Если у шлюза есть отдельные журналы сессий и операций, используйте оба. Запись сессии отвечает на вопрос, кто запустил процесс агента и остаётся ли этот запуск авторизованным. Журнал операций показывает, какие отдельные вызовы произошли и в каком порядке. Это разные вопросы, и объединение их в один широкий поток событий усложняет ответы в напряжённой ситуации.

Sallyport строит оба журнала из одного зашифрованного аудита с цепочкой хешей. После сохранения нужных записей сессии и операций выполните офлайн-проверку целостности:

```sh
sp audit verify
```

Сохраните точный результат команды вместе с записью об инциденте. Проверка не определяет, было ли действие разрешено или разумно. Она показывает, проходит ли проверку цепочка аудита, на которую вы опираетесь, без доступа к хранилищу. Это полезно, когда проверяющему инцидент не нужно выдавать учётные данные только ради просмотра истории.

Не ждите формального инцидента безопасности, чтобы отработать такие записи. Именно обычная проверка после сбоя обнаруживает отсутствующие идентификаторы сессий, неоднозначные команды и журналы, которые существуют только на погибшей машине. Устранить эти пробелы во время спокойной очистки намного дешевле, чем обнаружить их после записи в рабочую систему.

## Сначала отзовите права, затем завершайте процесс

Самый безопасный порядок такой: отозвать права, проверить отказ, затем остановить процесс. Обратный порядок кажется быстрее, потому что процесс исчезает сразу, но запись авторизации может остаться активной, а последующее сопоставление событий станет сложнее.

Начните с самого узкого эффективного отзыва. Если система отслеживает авторизацию для каждого запуска агента, отзовите этот запуск. Если серверу был передан отдельный секрет, отключите или замените его. Если у удалённой задачи есть дескриптор отмены, отмените задачу отдельно. Это разные действия, потому что они затрагивают разные сроки жизни.

Не считайте, что завершение сервера отменяет удалённую работу. Процесс может исчезнуть, а его запрос останется в очереди. Не считайте и обратное: отмена удалённой задачи не останавливает сервер. Если у него сохранились права, он может повторить запрос или отправить новый. Для containment нужны согласованные действия на локальной и удалённой сторонах.

После отзыва пути к действию проверьте состояние подходящими для вашей системы доказательствами. Это может быть записанный в журнале шлюза отказ, неудачный аутентифицированный запрос к безопасному эндпоинту идентичности или удалённая запись об отключении токена. Метод зависит от системы. Принцип один: докажите, что оставшийся процесс не может выполнить защищённый вызов.

Затем отправьте обычный сигнал завершения определённому PID:

```sh
kill -TERM 48271
sleep 2
ps -p 48271 -o pid=,ppid=,stat=,etime=,command=
```

Если последняя команда не вернула строку процесса, запишите это. Если процесс остался, проверьте, завершает ли он работу, заблокирован ли на вводе-выводе или удерживает дочернюю работу. Перед усилением воздействия ещё раз проверьте группу и дочерние процессы. Возможно, известный помощник придётся завершить отдельно, но не завершайте всю группу вслепую, пока не убедитесь, что каждый её участник относится к одному и тому же неудачному запуску.

Используйте `kill -KILL`, только если обычное завершение не сработало и доказательства уже сохранены. SIGKILL не даёт процессу закрыть файлы, отменить работу, записать финальный журнал или удалить временное состояние. Иногда это правильный компромисс. Называйте его точно: принудительное containment с возможной неполной очисткой.

Мониторинг активности в macOS может помочь, когда командной строки недостаточно. Apple документирует обычное завершение и принудительное завершение, а также позволяет показывать процессы иерархически. Иерархия полезна для подтверждения связей между родителем и потомками. Она не заменяет запись об инциденте, поскольку список в графическом интерфейсе не сохраняет сведения о сессии и действиях, которые понадобятся позже.

## Сделайте завершение сервера обязательной частью дизайна

Серверы stdio должны воспринимать исчезновение клиента как штатное событие, а не редкий крайний случай. Сбои клиентов это обычное поведение программ. Ноутбуки засыпают, терминалы закрываются, IDE перезапускаются, обновления прерывают процессы, а агенты могут остановиться после ошибки модели.

У сервера должен быть явный путь завершения с четырьмя свойствами. Он прекращает приём новых запросов после закрытия входа. Отслеживает каждое начатое действие с идентификатором корреляции. Даёт активной работе ограниченное время, чтобы отмениться или перейти в известное состояние. После этого срока завершается, а не случайно превращается в постоянную фоновую службу.

Не путайте корректное завершение с бесконечным ожиданием. Сервер, получивший EOF и бесконечно ожидающий внешний API, остаётся осиротевшим, только более вежливым. Установите крайний срок для очистки, запишите, что осталось нерешённым, и завершите процесс. Нерешённая работа должна находиться по внешнему идентификатору задачи или записи операции.

Управление дочерними процессами не менее важно. Если инструмент запускает компилятор, менеджер пакетов, SSH-помощник, драйвер браузера или оболочку-команду, сервер должен понимать, должен ли этот потомок завершиться вместе с сервером. Настройте группы процессов осознанно. Сохраняйте PID дочерних процессов. При завершении останавливайте только потомков, относящихся к запросу, и записывайте, завершились ли они.

Не отправляйте работу в фон через оболочку, если контракт инструмента явно не предусматривает долговечную фоновую работу. Команда вроде `some-command \u0026` создаёт второй срок жизни, который MCP-сервер может не отслеживать. Если нужна долговечная работа, отправьте её в систему задач, возвращающую ID задачи, а затем предоставьте отдельные инструменты для просмотра состояния и отмены. Скрытая фоновая работа это не надёжность. Это пробел в аудите.

Используйте идемпотентность, если её поддерживает удалённый сервис. Каждой внешней записи присваивайте идентификатор запроса, производный от сессии и вызова инструмента, и записывайте его до отправки запроса. Если клиент упадёт после отправки, вы сможете запросить состояние во внешней системе, а не гадать, нужно ли повторять операцию. Если идемпотентность не поддерживается, зафиксируйте неоднозначность и требуйте решения оператора перед повторной записью.

Сервер также должен писать диагностические сообщения в stderr, никогда не в stdout. Stdout принадлежит потоку JSON-RPC MCP, разделённому переводами строк. Случайная отладочная строка может повредить протокол, вызвать сбой клиента и создать именно тот сценарий, который вы пытаетесь очистить. Деталь кажется мелкой, пока рабочий сервер не напечатает при запуске предупреждение библиотеки.

## Храните сведения о владельце в записи действия, а не в истории оболочки

История оболочки удобна, пока она не исчезла, не обрезалась, не стала общей для нескольких пользователей или не была записана после завершения процесса. Запись действия должна содержать сведения о владельце в момент выполнения работы.

Для каждого вызова, способного затронуть внешнюю систему, добавьте достаточно контекста, чтобы позже ответить на четыре вопроса: какой запуск агента его запросил, какой локальный процесс его отправил, какое решение об авторизации его разрешило и какая внешняя операция стала результатом. Если отсутствует хотя бы один элемент, после сбоя клиента образуется пробел, который расследователю придётся заполнять предположениями.

Не пытайтесь использовать идентичность подписи кода для ответа на все вопросы о владельце. Она показывает, кто подписал исполняемый файл, что полезно при решении, заслуживает ли процесс одобрения. Но она не указывает конкретный запуск, запрос, приведший к вызову, или удалённый запрос. Используйте полномочия подписи кода для доверия при начале сессии, а для операционной прослеживаемости применяйте идентификатор сессии и записи отдельных вызовов.

То же предупреждение относится к командным строкам. Команда может показать, что запущены `sp mcp` или исполняемый файл сервера. Она не может надёжно сказать, какой вызов инструмента был последним, одобрил ли его пользователь и приняла ли его удалённая система. Считайте список процессов вспомогательным доказательством, а не источником аудита.

При сбое выполняйте сопоставление в таком порядке:

1. Найдите запуск агента, которому принадлежал клиентский процесс в момент сбоя.
2. Найдите процесс сервера или группу процессов, запущенную этим запуском.
3. Найдите последние записи операций этого запуска и сопоставьте их время со снимком процессов.
4. Сверьте незавершённую внешнюю операцию по идентификатору запроса, транзакции или задачи.
5. Запишите события отзыва и завершения рядом с последними вызовами.

Такая последовательность предотвращает распространённую ошибку: найти устаревший PID, завершить его, а потом приписать последний API-вызов не тому запуску агента, потому что две сессии использовали одну и ту же команду сервера. Команды повторяются. Записи сессий должны быть уникальными.

Если сейчас ваша среда не может выполнить такое сопоставление, добавьте его до выдачи автономным инструментам доступа на запись. Для экспериментов только с чтением несовершенная наблюдаемость может быть приемлема. Для изменений в рабочей системе нет.

## Учебный сбой выявляет пробелы, пока цена ошибки невелика

Проведите контролируемую проверку сбоя для каждого MCP-сервера, способного менять внешние системы. Делайте это в тестовом аккаунте или с доступом только к операциям чтения и собирайте доказательства так, будто работаете с настоящим инцидентом.

Запустите обычную клиентскую сессию и выполните одно действие с известным идентификатором корреляции. Пока сервер работает, резко завершите клиент. Затем проверьте PID сервера, его PPID и группу процессов, открытые дескрипторы и журнал операций. Отзовите сессию. Убедитесь, что последующий защищённый вызов отклонён. Наконец, завершите сервер, если он не остановился после EOF, и сопоставьте внешнюю операцию.

Проверка должна давать ответы, а не простой ярлык «пройдено» или «не пройдено». Нужно узнать, доходит ли закрытие stdin до сервера, остаются ли дочерние процессы, есть ли у удалённых задач дескрипторы отмены, можно ли определить владельца по журналам и меняет ли отзыв прав поведение сразу.

Следите за временем. Сбой до отправки запроса ведёт себя иначе, чем сбой после того, как удалённый сервис принял запрос. Если сервер предоставляет достаточно инструментов наблюдения, повторите проверку в обеих точках. Именно неприятная граница между «отправлено» и «подтверждено» порождает дублирующие записи и ложные заверения.

Для каждого сервера запишите ожидаемый порядок очистки. Хорошее ожидание конкретно: после закрытия входа сервер перестаёт принимать вызовы, завершается в пределах заданного тайм-аута, не оставляет принадлежащих ему помощников и создаёт запись операции для каждого начатого внешнего действия. Слабое ожидание звучит так: «клиент обычно сам всё очищает». Обычное поведение не является контролем.

После нескольких таких проверок устаревшие процессы перестанут быть загадкой. Они превратятся в определённый сценарий сбоя со снимком процесса, цепочкой владельца, действием по отзыву прав и правилом очистки. К этому стандарту и нужно стремиться. Процесс может завершиться с ошибкой. Ваша способность объяснить и локализовать его последние вызовы не должна.
