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

8.0 KiB

id ticket title status created accepted ref_agenda tags
DEC-0027 task-owned-shell-windows Decision - Task-Owned Shell Windows accepted 2026-05-15 2026-05-15 AGD-0035
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:

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:

TaskState::Foreground
+
focused window owner == WindowOwner::Task(task_id)

O predicado de janela MUST ser exposto pela WindowFacade como:

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:

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:

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:

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.