--- id: AGD-0035 ticket: task-owned-shell-windows title: Agenda - Task-Owned Shell Windows status: accepted created: 2026-05-15 resolved: 2026-05-15 decision: DEC-0027 tags: [runtime, os, task, window-manager, shell, lifecycle] --- # Agenda - Task-Owned Shell Windows ## Contexto O Prometeu OS ja possui `SystemOS` como autoridade semantica para coordenar lifecycle entre `Task` e `Process`, com facades por dominio para evitar que o firmware manipule storage interno diretamente. O modelo atual separa: ```text Process = execucao tecnica Task = presenca navegavel Window = presenca visual da task ``` O `WindowOwner` ja representa `Hub`, `Task(TaskId)` e `Overlay`. Shell apps ja conseguem criar janelas associadas a uma task, e games continuam fora do `WindowManager`: eles sao sessoes fullscreen conduzidas por `GameRunningStep`, nao janelas. Hoje, porem, `ShellRunningStep` ainda decide se continua rodando a partir de um sinal generico: `focused_window_active`. Esse sinal vem do resultado do update do Hub e apenas indica se existe alguma janela focada/ativa, sem confirmar que essa janela pertence a task que o `ShellRunningStep` esta conduzindo. ## Problema O contrato de liveness de uma Shell task ainda esta acoplado a presenca visual generica, nao a presenca visual da propria task. Isso deixa uma lacuna conceitual: - uma task pode estar em `Foreground`, mas a janela focada pode pertencer a outro owner; - uma janela pode estar focada, mas nao representar a task que esta rodando no `ShellRunningStep`; - o firmware precisa inferir continuidade por um booleano generico em vez de perguntar ao OS: "esta task ainda possui uma janela ativa/focada?". Se `Task` e a unidade de navegacao, e `Window` e a presenca visual dessa task, o fluxo de Shell precisa saber se a presenca visual pertence a mesma task. ## Pontos Criticos 1. Autoridade do contrato. A pergunta deve provavelmente passar por `SystemOS` ou por uma facade de windows/lifecycle, nao por logica espalhada no Hub. O `WindowManager` pode armazenar e consultar janelas, mas a semantica de "task continua viva" deve permanecer coerente com o modelo de lifecycle. 2. Foco versus atividade. A decisao precisa separar: - "a task possui alguma janela"; - "a task possui uma janela ativa"; - "a task possui a janela atualmente focada". Para o `ShellRunningStep`, o criterio candidato e mais forte: a janela focada deve pertencer a propria task. 3. Encerramento da task. Se uma Shell task perde sua unica janela ativa/focada, o sistema precisa decidir se isso fecha a task, suspende a task, retorna ao Hub, ou representa erro. Para esta agenda, o foco e o contrato Task-Window; politicas futuras de background ficam fora. 4. Separacao entre Shell e Game. Games nao devem virar `WindowOwner::Task`. A presenca visual de game continua sendo a propria sessao fullscreen do `GameRunningStep`. O contrato aqui vale para Shell/system apps representados por janelas. 5. Multiplicidade futura sem resolver agora. O contrato deve ser simples o bastante para uma janela por Shell task hoje, mas nao deve bloquear uma evolucao posterior para multiplas janelas. A agenda nao deve decidir dock, app switcher, services em background, minimizacao, ou redesign da Shell. ## Opcoes ### Opcao A - Manter `focused_window_active` como criterio generico **Abordagem:** `ShellRunningStep` continua dependendo do outcome generico do Hub, que informa apenas se existe alguma janela focada/ativa. **Vantagens:** - menor mudanca imediata; - preserva o fluxo atual de retorno ao Hub; - evita adicionar API nova no `WindowManager` agora. **Riscos / Custos:** - nao expressa o modelo `Task = presenca navegavel` e `Window = presenca visual da task`; - permite continuidade baseada em janela de outro owner; - mantem uma inferencia de lifecycle fora do contrato direto do OS; - tende a piorar quando houver mais janelas ou owners. **Leitura:** opcao aceitavel apenas como estado transitorio, nao como contrato final. ### Opcao B - `ShellRunningStep` depende de janela focada pertencente a propria task **Abordagem:** expor uma consulta do tipo `focused_window_belongs_to_task(task_id)` ou `task_has_focused_window(task_id)`. O `ShellRunningStep` continua somente se a task estiver `Foreground` e a janela focada tiver `WindowOwner::Task(task_id)`. **Vantagens:** - alinha diretamente `Task` e `Window`; - remove a ambiguidade do booleano generico; - torna o encerramento de Shell task explicavel: sem janela focada da propria task, nao ha presenca visual ativa para aquela task; - funciona bem para o modelo atual de uma Shell task em foreground. **Riscos / Custos:** - pode ser forte demais se o futuro permitir task com janela existente mas temporariamente sem foco; - exige definir se perda de foco para `Hub` ou `Overlay` fecha a task ou apenas pausa o fluxo; - precisa tomar cuidado para overlays nao roubarem foco de forma que fechem a task acidentalmente. **Leitura:** e a opcao mais coerente para o contrato imediato, desde que overlays e Hub sejam tratados explicitamente. ### Opcao C - `ShellRunningStep` depende de qualquer janela ativa pertencente a propria task **Abordagem:** expor uma consulta do tipo `task_has_active_window(task_id)`. Foco global continua separado: a task segue viva enquanto pelo menos uma janela aberta/ativa pertence a ela, mesmo que outra superficie esteja focada. **Vantagens:** - prepara melhor um futuro com overlays, app switcher ou multiplas janelas; - evita fechar task por perda temporaria de foco; - separa "existencia visual da task" de "janela no topo". **Riscos / Custos:** - exige definir imediatamente o que significa "active" alem de `has_focus`; - pode preservar tasks sem presenca navegavel real se a janela existir mas nao estiver selecionada; - abre portas para semantica de background/minimizacao, que esta fora do escopo desta agenda. **Leitura:** boa direcao futura, mas talvez maior que o contrato necessario agora. ### Opcao D - Lifecycle fecha janelas; janelas nunca fecham lifecycle **Abordagem:** `ShellRunningStep` ignora foco/janelas para continuidade; a task so fecha quando o lifecycle manda fechar. O `WindowManager` seria apenas reflexo visual da task, nao sinal de liveness. **Vantagens:** - preserva autoridade total do lifecycle; - evita que bugs de foco encerrem task; - simples de raciocinar em termos de `TaskState`. **Riscos / Custos:** - contradiz a intencao de `Window = presenca visual da task` para Shell apps; - pode manter uma Shell task foreground sem janela visivel; - empurra para outro mecanismo a deteccao de fechamento pelo usuario. **Leitura:** defensavel para services/background no futuro, mas desalinhada com Shell apps visuais neste escopo. ## Sugestao / Recomendacao A recomendacao consolidada e seguir a Opcao B como contrato v1: ```text ShellRunningStep continua somente quando: 1. sua Task esta em Foreground; 2. a janela focada pertence a WindowOwner::Task(task_id) da propria task. ``` Isso troca o sinal generico `focused_window_active` por uma pergunta explicita ao `WindowFacade`: ```text os.windows().focused_window_belongs_to_task(task_id) ``` Esse nome comunica que o `WindowManager` possui um foco global, e que a pergunta e se esse foco global pertence a task informada. O nome `task_has_focused_window` continua sendo conceitualmente aceitavel, mas pode soar como se cada task tivesse um foco proprio. O comportamento v1 recomendado e: ```text if !ctx.os.windows().focused_window_belongs_to_task(self.task_id) { ctx.os.lifecycle().close_task(self.task_id)?; return Some(FirmwareState::HubHome(HubHomeStep)); } ``` O contrato deve declarar tambem: - `Hub` e `Overlay` nao sao Shell task windows; - games nao entram no `WindowManager`; - a ausencia de janela focada da propria Shell task fecha ou encerra o fluxo da Shell task conforme a politica atual, sem introduzir background service ou minimizacao nesta decisao; - multiplas janelas, dock, app switcher, background services e redesign da Shell ficam fora desta agenda. ## Perguntas em Aberto - [x] O criterio v1 deve ser "janela focada pertence a task" ou "task possui alguma janela ativa"? - [x] O nome da API deve falar em foco (`task_has_focused_window`) ou em pertencimento do foco global (`focused_window_belongs_to_task`)? - [x] Quando uma `Overlay` esta focada, ela deve preservar a Shell task subjacente ou isso ainda nao existe no modelo atual? - [x] Fechar a ultima janela da Shell task deve chamar `close_task(task_id)` diretamente ou apenas retornar ao Hub e deixar lifecycle resolver? - [x] A consulta deve morar no `WindowFacade`, em uma facade de lifecycle, ou em uma facade composta de `SystemOS`? - [x] A decision posterior precisa alterar somente firmware/system, ou tambem documentar esse contrato nos specs canonicos? ## Respostas Consolidadas ### Criterio v1 O criterio v1 e "janela focada pertence a task". `ShellRunningStep` continua somente quando: ```text TaskState::Foreground + focused window owner == WindowOwner::Task(task_id) ``` A alternativa "task possui alguma janela ativa" fica reservada para um futuro com overlays, multiplas janelas, minimizacao ou app switcher. Ela e mais ampla do que o contrato necessario agora. Nesta versao, Shell apps sao foreground e visualmente representados por uma janela focada. Se a janela focada nao pertence a task, a task nao deve continuar executando. ### Nome e local da API O nome recomendado e: ```text focused_window_belongs_to_task(task_id) ``` A API deve morar em `WindowFacade`: ```text os.windows().focused_window_belongs_to_task(task_id) ``` Motivo: a pergunta e sobre foco, janela e `WindowOwner`, nao sobre transicao de lifecycle. A separacao de responsabilidades fica: ```text WindowFacade responde perguntas sobre janelas, foco e ownership LifecycleFacade altera TaskState e ProcessState ShellRunningStep coordena a decisao firmware-level entre janela e lifecycle ``` Se esse padrao se repetir, uma facade composta como `sessions()` pode surgir no futuro, mas ela nao e necessaria para este contrato v1. ### Overlays Overlay policy ainda nao existe no modelo atual. Para v1: ```text Hub nao e janela da task. Overlay nao e janela da task. Game nao e WindowManager window. ShellRunningStep exige foco em WindowOwner::Task(task_id). ``` Se uma overlay puder roubar foco, isso precisa ser tratado em uma agenda propria de overlay/modal policy. Esta decisao nao deve tentar preservar, pausar, ou modelar Shell task sob overlay. Possiveis direcoes futuras, fora deste escopo: - overlay modal preserva a task subjacente; - overlay nao-modal nao rouba foco; - overlay possui owner/contexto proprio. ### Fechamento da Shell task Quando a Shell task perde sua janela focada elegivel, o firmware deve chamar: ```text os.lifecycle().close_task(task_id) ``` e entao retornar para `HubHome`. Nao basta apenas retornar ao Hub, porque isso deixaria task/processo dependente de cleanup implicito. Contrato v1: ```text Shell task perde sua janela focada elegivel -> lifecycle.close_task(task_id) -> TaskState::Closed -> ProcessState::Stopped -> FirmwareState::HubHome ``` O fechamento visual da Shell task precisa passar pelo lifecycle do `SystemOS`. ### Propagacao para specs A decision posterior deve alterar firmware/system e tambem documentar o contrato nos specs canonicos. O contrato arquitetural a preservar e: ```text Process = execucao tecnica Task = presenca navegavel Window = presenca visual da task ``` Tambem deve ficar documentado: - games nao sao janelas; - games rodam como sessoes fullscreen via `GameRunningStep`; - Shell apps possuem janelas no `WindowManager`; - Shell app windows usam `WindowOwner::Task(TaskId)`; - `Hub` e `Overlay` nao sao janelas de Shell task; - na v1, `ShellRunningStep` so continua se a janela focada pertence a sua propria task. Isso evita que trabalho futuro transforme game em window ou reintroduza `focused_window_active` como contrato generico. ## Fora de Escopo - dock; - app switcher; - background services; - minimizacao; - multiplas janelas complexas; - redesign da Shell; - transformar games em janelas; - mudar o modelo fullscreen de `GameRunningStep`; - reabrir o contrato geral de `Process`, `Task` e lifecycle ja consolidado. ## Criterio para Encerrar Esta agenda pode virar decision quando houver resposta explicita para: 1. qual predicado `ShellRunningStep` usa para continuar; 2. onde a API de consulta Task-Window mora; 3. como `Hub`, `Overlay` e `GameRunningStep` ficam excluidos ou tratados; 4. qual comportamento ocorre quando a Shell task perde sua janela elegivel; 5. quais specs ou codigos precisam ser propagados depois da decision. ## Discussion A discussao convergiu para um contrato v1 pequeno, focado em Shell task foreground com janela focada propria. Resumo da direcao: ```text AGD-0035 segue a Opcao B. Predicado: focused_window_belongs_to_task(task_id) Facade: os.windows() Comportamento: se falso em ShellRunningStep: os.lifecycle().close_task(task_id) retorna HubHome Fora de escopo: overlay policy background minimizacao app switcher game windows ``` Casos de background, overlay policy, minimizacao, app switcher e multiplas janelas devem ficar para agendas futuras. ## Resolution Aceita em 2026-05-15 e promovida para `DEC-0027`. A decision deve seguir a Opcao B: `ShellRunningStep` continua somente quando a Shell task esta em `TaskState::Foreground` e a janela focada pertence a `WindowOwner::Task(task_id)`. A API normativa e `os.windows().focused_window_belongs_to_task(task_id)`. Quando o predicado for falso, `ShellRunningStep` deve fechar a task via `os.lifecycle().close_task(task_id)` e retornar `HubHome`.