prometeu-runtime/discussion/workflow/agendas/AGD-0035-agenda-task-owned-shell-windows.md
2026-05-15 11:43:04 +01:00

433 lines
14 KiB
Markdown

---
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`.