prometeu-runtime/discussion/workflow/decisions/DEC-0027-decision-task-owned-shell-windows.md
2026-05-15 11:43:04 +01:00

239 lines
8.0 KiB
Markdown

---
id: DEC-0027
ticket: task-owned-shell-windows
title: Decision - Task-Owned Shell Windows
status: accepted
created: 2026-05-15
accepted: 2026-05-15
ref_agenda: AGD-0035
tags: [runtime, os, task, window-manager, shell, lifecycle]
---
# Decision - Task-Owned Shell Windows
## Status
Accepted on 2026-05-15. This decision is normative and ready to drive plans.
## Contexto
O Prometeu OS ja separa lifecycle tecnico e presenca navegavel:
```text
Process = execucao tecnica
Task = presenca navegavel
Window = presenca visual da task
```
`SystemOS` e a autoridade semantica para coordenar `TaskState` e
`ProcessState`. `TaskManager`, `ProcessManager` e `WindowManager` continuam
como storage/servicos de dominio, acessados por facades.
`WindowOwner` ja representa `Hub`, `Task(TaskId)` e `Overlay`. Shell apps podem
criar janelas com `WindowOwner::Task(task_id)`. Games continuam fora do
`WindowManager`: eles sao sessoes fullscreen conduzidas por `GameRunningStep`,
nao janelas.
Antes desta decision, `ShellRunningStep` dependia de um sinal generico,
`focused_window_active`, que apenas informava se existia alguma janela focada.
Esse contrato nao garantia que a janela focada pertencia a task que o
`ShellRunningStep` estava conduzindo.
## Decisao
`ShellRunningStep` MUST continuar rodando uma Shell task somente quando as duas
condicoes abaixo forem verdadeiras:
```text
TaskState::Foreground
+
focused window owner == WindowOwner::Task(task_id)
```
O predicado de janela MUST ser exposto pela `WindowFacade` como:
```text
os.windows().focused_window_belongs_to_task(task_id)
```
Esse predicado MUST consultar o foco global do `WindowManager` e responder se a
janela atualmente focada pertence a `WindowOwner::Task(task_id)`.
Quando `ShellRunningStep` detectar que a Shell task foreground nao possui a
janela focada elegivel, ele MUST fechar a task por lifecycle antes de retornar
ao Hub:
```text
Shell task perde sua janela focada elegivel
-> os.lifecycle().close_task(task_id)
-> TaskState::Closed
-> ProcessState::Stopped
-> FirmwareState::HubHome
```
O firmware MUST NOT apenas retornar ao Hub sem acionar
`os.lifecycle().close_task(task_id)`, porque isso deixaria a task/processo
dependente de cleanup implicito.
## Rationale
O modelo do OS precisa preservar a relacao direta entre task navegavel e janela
visual. Se uma Shell task esta em foreground, sua continuidade precisa depender
da janela visual da propria task, nao da existencia de qualquer janela focada.
`focused_window_belongs_to_task(task_id)` e o nome escolhido porque o foco e
global no `WindowManager`. A pergunta nao e se a task possui um subsistema de
foco proprio, mas se o foco global atual pertence aquela task.
A consulta pertence a `WindowFacade` porque ela responde sobre janela, foco e
ownership. Transicoes de estado continuam pertencendo a `LifecycleFacade`.
`ShellRunningStep` coordena os dois dominios no nivel firmware:
```text
WindowFacade
responde perguntas sobre janelas, foco e ownership
LifecycleFacade
altera TaskState e ProcessState
ShellRunningStep
coordena a decisao firmware-level entre janela e lifecycle
```
Essa separacao preserva o desenho recente de `SystemOS`: ownership e mediacao
continuam centralizados, mas cada facade responde pelo dominio certo.
## Alternativas Descartadas
### Manter `focused_window_active`
Descartado como contrato final porque o booleano generico nao codifica
ownership da task. Ele permitiria que uma Shell task continuasse por causa de
uma janela pertencente ao `Hub`, a uma `Overlay`, ou a outra task futura.
### Usar "task possui alguma janela ativa"
Descartado para v1 porque abre semantica de overlays, multiplas janelas,
minimizacao, app switcher e background. Essa direcao pode ser reavaliada em
agenda futura, mas e maior que o contrato necessario agora.
### Lifecycle fecha janelas, mas janelas nunca afetam lifecycle
Descartado para Shell apps foreground porque permitiria uma Shell task
foreground sem presenca visual propria. Isso contradiz o contrato
`Window = presenca visual da task` para apps Shell.
## Invariantes / Contrato
- `Process` MUST representar execucao tecnica.
- `Task` MUST representar presenca navegavel.
- `Window` MUST representar presenca visual de Shell/system tasks quando essas
tasks sao modeladas pelo `WindowManager`.
- Shell app windows MUST usar `WindowOwner::Task(TaskId)`.
- `Hub` MUST NOT ser tratado como janela de Shell task.
- `Overlay` MUST NOT ser tratado como janela de Shell task nesta v1.
- Games MUST NOT ser modelados como `WindowManager` windows.
- Games MUST continuar como sessoes fullscreen conduzidas por
`GameRunningStep`.
- `ShellRunningStep` MUST NOT usar `focused_window_active` como contrato de
continuidade da sua task.
- `ShellRunningStep` MUST consultar
`os.windows().focused_window_belongs_to_task(task_id)` para verificar
ownership da janela focada.
- Quando a consulta retornar falso para uma Shell task foreground,
`ShellRunningStep` MUST chamar `os.lifecycle().close_task(task_id)` e retornar
`FirmwareState::HubHome`.
- `WindowFacade` MUST responder perguntas de foco, janela e ownership.
- `LifecycleFacade` MUST executar mudancas de `TaskState` e `ProcessState`.
- Uma futura politica de overlay/modal MUST ser decidida em agenda propria se
overlays passarem a roubar foco ou preservar task subjacente.
## Impactos
### Spec
Os specs canonicos MUST documentar o contrato:
```text
Process = execucao tecnica
Task = presenca navegavel
Window = presenca visual da task
```
Eles tambem MUST documentar que games nao sao janelas, que games rodam via
`GameRunningStep`, que Shell apps usam `WindowOwner::Task(TaskId)`, e que v1 de
`ShellRunningStep` depende de foco pertencente a propria task.
### Runtime / System
`WindowFacade` precisa expor `focused_window_belongs_to_task(task_id)`.
`WindowManager` pode continuar sendo o storage de foco e janelas, mas a API
publica consumida pelo firmware deve passar pela facade.
### Firmware
`ShellRunningStep` deve substituir o uso de `focused_window_active` pela nova
consulta em `os.windows()`. Quando o predicado for falso, deve fechar a task via
`os.lifecycle().close_task(task_id)` e retornar `HubHome`.
`GameRunningStep` nao muda por esta decision.
### Host
Sem impacto direto esperado. O host nao deve precisar conhecer o contrato
Task-Window para Shell task liveness.
### Firmware / Hub
O outcome generico `focused_window_active` deixa de ser o contrato de
continuidade de `ShellRunningStep`. Ele pode permanecer como detalhe interno ou
ser removido em plano posterior, desde que nao seja usado como predicado de
liveness da Shell task.
### Tooling
Sem impacto direto esperado alem de testes.
## Propagacao Necessaria
- Criar plano de execucao a partir desta decision antes de editar specs ou
codigo.
- Atualizar specs canonicos em ingles com o contrato Task-Window.
- Adicionar `focused_window_belongs_to_task(task_id)` em `WindowFacade` e, se
fizer sentido para teste unitario, em `WindowManager`.
- Alterar `ShellRunningStep` para consultar a nova API e fechar lifecycle antes
de retornar `HubHome`.
- Ajustar ou remover o uso de `focused_window_active` no caminho de Shell
running.
- Adicionar testes para:
- janela focada da propria task permite continuidade;
- janela focada de outro owner fecha a Shell task;
- ausencia de foco fecha a Shell task;
- fechamento passa por `LifecycleFacade` e para o processo;
- games continuam fora do `WindowManager`.
## Fora de Escopo
- overlay/modal policy;
- background services;
- minimizacao;
- app switcher;
- dock;
- multiplas janelas complexas;
- redesign da Shell;
- transformar games em janelas;
- reabrir o contrato geral de `Process`, `Task` e lifecycle.
## Referencias
- Agenda: `AGD-0035`
- Discussao: `DSC-0035`
- Licao relacionada: `LSN-0040` System Pipeline Separation
- Licao relacionada: `LSN-0041` SystemOS Lifecycle Authority
- Licao relacionada: `LSN-0042` SystemOS Service Ownership Boundary
- Licao relacionada: `LSN-0043` SystemOS Domain Facades
## Revision Log
- 2026-05-15: Draft inicial criado a partir de `AGD-0035`.