@softize/opus 18.0.1 → 18.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/PROMOTED.md +4 -5
  3. package/README.md +5 -4
  4. package/bin/cli.mjs +4 -0
  5. package/docs/adr/0004-page-content-state-is-composed.md +3 -0
  6. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +6 -2
  7. package/docs/adr/0011-page-shell-coordinates-persistent-page-chrome.md +6 -1
  8. package/docs/adr/0014-structural-headers-do-not-carry-description.md +2 -2
  9. package/docs/adr/0015-action-size-follows-interaction-density.md +4 -3
  10. package/docs/adr/0017-ui-assumes-a-fixed-desktop-layout.md +54 -0
  11. package/docs/adr/{0012-modal-header-only-names-the-surface.md → 0018-modal-header-only-names-the-surface.md} +4 -1
  12. package/docs/adr/0019-productive-surfaces-use-compact-density.md +65 -0
  13. package/docs/code-style.md +2 -2
  14. package/docs/consumer-upgrade-propagation.md +1 -1
  15. package/docs/data-products.md +5 -3
  16. package/docs/protocol.md +6 -6
  17. package/docs/relative-unit-scale.md +9 -2
  18. package/docs/releasing.md +28 -4
  19. package/package.json +1 -1
  20. package/registry/skills/build-opus-ui/references/evaluations.md +3 -1
  21. package/registry/skills/build-opus-ui/references/ui-patterns.md +20 -4
  22. package/src/auth/drivers/jwt.ts +2 -1
  23. package/src/core/runtime.ts +32 -5
  24. package/src/core/types.ts +16 -7
  25. package/src/mcp/index.ts +13 -1
  26. package/src/ui/components/patterns/action-list-dialog.tsx +2 -2
  27. package/src/ui/components/patterns/content-header.tsx +2 -2
  28. package/src/ui/components/patterns/form-dialog.tsx +7 -2
  29. package/src/ui/components/patterns/form.tsx +1 -1
  30. package/src/ui/components/patterns/list.tsx +239 -47
  31. package/src/ui/components/patterns/presentation.tsx +7 -5
  32. package/src/ui/components/patterns/sidebar.tsx +1 -1
  33. package/src/ui/components/patterns/state-surface.tsx +2 -2
  34. package/src/ui/components/patterns/surface-header.tsx +4 -4
  35. package/src/ui/components/primitives/alert.tsx +2 -2
  36. package/src/ui/components/primitives/breadcrumb.tsx +1 -1
  37. package/src/ui/components/primitives/button-group.tsx +1 -1
  38. package/src/ui/components/primitives/button.tsx +3 -3
  39. package/src/ui/components/primitives/calendar.tsx +1 -1
  40. package/src/ui/components/primitives/card.tsx +1 -1
  41. package/src/ui/components/primitives/close-button.tsx +40 -0
  42. package/src/ui/components/primitives/detail.tsx +68 -30
  43. package/src/ui/components/primitives/dialog.tsx +36 -21
  44. package/src/ui/components/primitives/drawer.tsx +26 -19
  45. package/src/ui/components/primitives/empty-value.tsx +3 -3
  46. package/src/ui/components/primitives/empty.tsx +1 -1
  47. package/src/ui/components/primitives/field.tsx +12 -12
  48. package/src/ui/components/primitives/icon-picker.tsx +1 -1
  49. package/src/ui/components/primitives/input-group.tsx +1 -1
  50. package/src/ui/components/primitives/input.tsx +2 -2
  51. package/src/ui/components/primitives/item.tsx +5 -5
  52. package/src/ui/components/primitives/pagination.tsx +4 -4
  53. package/src/ui/components/primitives/radio-group.tsx +1 -1
  54. package/src/ui/components/primitives/select.tsx +3 -3
  55. package/src/ui/components/primitives/table.tsx +26 -17
  56. package/src/ui/components/primitives/tabs.tsx +80 -23
  57. package/src/ui/components/primitives/textarea.tsx +1 -1
  58. package/src/ui/components/primitives/toggle-group.tsx +9 -2
  59. package/src/ui/docs/content/action-form-dialog.md +11 -4
  60. package/src/ui/docs/content/action-form.md +13 -3
  61. package/src/ui/docs/content/action-list-dialog.md +5 -7
  62. package/src/ui/docs/content/action-list.md +53 -5
  63. package/src/ui/docs/content/action-trigger.md +9 -5
  64. package/src/ui/docs/content/action-view.md +12 -8
  65. package/src/ui/docs/content/actions.md +36 -13
  66. package/src/ui/docs/content/ai.md +26 -7
  67. package/src/ui/docs/content/alert.md +6 -3
  68. package/src/ui/docs/content/aspect-ratio.md +2 -2
  69. package/src/ui/docs/content/auth.md +25 -10
  70. package/src/ui/docs/content/avatar.md +1 -1
  71. package/src/ui/docs/content/badge.md +2 -2
  72. package/src/ui/docs/content/breadcrumb.md +3 -2
  73. package/src/ui/docs/content/button.md +33 -8
  74. package/src/ui/docs/content/calendar.md +1 -1
  75. package/src/ui/docs/content/card.md +1 -1
  76. package/src/ui/docs/content/carousel.md +14 -3
  77. package/src/ui/docs/content/chat.md +1 -1
  78. package/src/ui/docs/content/cli.md +13 -7
  79. package/src/ui/docs/content/command.md +34 -2
  80. package/src/ui/docs/content/composer.md +1 -1
  81. package/src/ui/docs/content/content.md +5 -4
  82. package/src/ui/docs/content/customization.md +12 -2
  83. package/src/ui/docs/content/cycle.md +7 -5
  84. package/src/ui/docs/content/data-state.md +6 -5
  85. package/src/ui/docs/content/data.md +3 -3
  86. package/src/ui/docs/content/detail.md +12 -10
  87. package/src/ui/docs/content/dialog.md +14 -7
  88. package/src/ui/docs/content/dictionary-value.md +1 -1
  89. package/src/ui/docs/content/dock.md +23 -2
  90. package/src/ui/docs/content/dot.md +0 -2
  91. package/src/ui/docs/content/drawer.md +7 -4
  92. package/src/ui/docs/content/empty-value.md +4 -4
  93. package/src/ui/docs/content/empty.md +1 -4
  94. package/src/ui/docs/content/events.md +1 -1
  95. package/src/ui/docs/content/field.md +21 -12
  96. package/src/ui/docs/content/getting-started.md +4 -2
  97. package/src/ui/docs/content/icon-picker.md +2 -2
  98. package/src/ui/docs/content/input-otp.md +2 -0
  99. package/src/ui/docs/content/input.md +2 -3
  100. package/src/ui/docs/content/item.md +6 -3
  101. package/src/ui/docs/content/kbd.md +2 -1
  102. package/src/ui/docs/content/mcp.md +10 -4
  103. package/src/ui/docs/content/menu.md +27 -0
  104. package/src/ui/docs/content/page.md +20 -6
  105. package/src/ui/docs/content/pagination.md +9 -2
  106. package/src/ui/docs/content/popover.md +2 -2
  107. package/src/ui/docs/content/presentation.md +48 -47
  108. package/src/ui/docs/content/progress.md +2 -6
  109. package/src/ui/docs/content/runtime.md +8 -5
  110. package/src/ui/docs/content/scheduler.md +1 -1
  111. package/src/ui/docs/content/select.md +13 -8
  112. package/src/ui/docs/content/sidebar.md +3 -2
  113. package/src/ui/docs/content/skeleton.md +1 -1
  114. package/src/ui/docs/content/slider.md +4 -4
  115. package/src/ui/docs/content/spinner.md +3 -3
  116. package/src/ui/docs/content/tabs.md +22 -12
  117. package/src/ui/docs/content/testing.md +4 -2
  118. package/src/ui/docs/content/toast.md +5 -6
  119. package/src/ui/docs/content/toggle.md +37 -0
  120. package/src/ui/docs/content/tokens.md +45 -2
  121. package/src/ui/docs/content/tooltip.md +4 -3
  122. package/src/ui/docs/content/truncate.md +3 -2
  123. package/src/ui/docs/content/ui.md +3 -1
  124. package/src/ui/docs/content/upgrading.md +43 -13
  125. package/src/ui/docs/doc-client.tsx +1 -1
  126. package/src/ui/docs/registry.tsx +30 -5
  127. package/src/ui/meta.ts +4 -4
  128. package/src/ui/react.tsx +1 -0
  129. package/src/ui/theme.css +3 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,109 @@ Depois de qualquer bump, rode os gates (`typecheck` · `test` · `opus check` ·
7
7
  `opus copy --check` · `base copy check` · `manifest:check`) — eles apontam o que a
8
8
  mudança cobra do seu código.
9
9
 
10
+ ## 18.1.1 — 2026-09-16
11
+
12
+ **Correção de segurança.** Quem executa tools de IA só roda o que `aiTools()` anunciou. Até aqui, o
13
+ servidor MCP executava em `CallTool` qualquer action registrada, e o agente co-locado executava
14
+ qualquer nome de tool que o modelo emitisse. Um cliente MCP, um modelo ou um prompt injetado podia
15
+ rodar uma action sem `ai.enabled` — inclusive destrutiva — dentro da autorização do contexto
16
+ resolvido. Os dois caminhos agora recusam o nome fora do conjunto exposto, com a mesma resposta
17
+ para action inexistente e para action registrada mas não exposta, sem revelar quais existem.
18
+ Nenhuma mudança é necessária em quem já marcava com `ai.enabled` as actions que expõe; quem
19
+ dependia de chamar pelo MCP uma action não marcada precisa marcá-la.
20
+
21
+ Uma action que não é `public` passa a exigir usuário ANTES de rodar os loaders. Antes, os
22
+ `loads` consultavam dados para um chamador anônimo, que recebia o erro do loader — um
23
+ `not_found`, por exemplo — e podia usar essa diferença para descobrir se um registro existe. O
24
+ `authorize` continua depois dos loaders, porque depende do que eles carregam.
25
+
26
+ `RadioGroupItem` passa a ter a classe `peer`, como `Checkbox` e `Switch`: o `Label` associado
27
+ esmaece quando o item está desabilitado, como a documentação já descrevia. Em `DetailGroup` com
28
+ moldura e orientação horizontal, a largura padrão da coluna de rótulos passa a ser uma classe, e
29
+ uma classe do consumidor (`[--detail-label-width:9rem]`) volta a substituí-la; antes, só `style`
30
+ conseguia.
31
+
32
+ A documentação publicada foi revisada por inteiro, e as 92 páginas foram abertas num navegador
33
+ headless sem erro de console nem de preview. O exemplo principal de Presentation voltou a
34
+ renderizar: ele usava funções que não estavam no escopo dos previews e lançava `ReferenceError`.
35
+ Um exemplo do `IconPicker` declarava `Icon`, nome que o escopo já fornece, e mostrava um erro de
36
+ sintaxe no lugar do preview. Um gate novo compila cada preview com o escopo da sua página, do jeito
37
+ que o site o executa, e reprova nome ausente ou redeclarado; o anterior só conferia tags JSX. Sessenta e quatro páginas foram
38
+ corrigidas contra o código: props, defaults e tipos que não batiam, exemplos que não compilavam ou ensinavam padrões
39
+ substituídos pelas versões 16 a 18, e afirmações que o código não sustenta. Entre elas, `auth.md`
40
+ deixa de sugerir que o runtime aplica um campo `auth` inexistente, e `upgrading.md` passa a incluir
41
+ `pnpm run setup` e os gates de copy, sem os quais o fluxo de upgrade falhava no primeiro gate.
42
+
43
+ `opus check --help` lista as regras de Produto de Dados. As ADRs que dividiam número foram
44
+ renumeradas: a do cabeçalho modal passa a 0018 e a da densidade compacta, a 0019; um gate impede
45
+ nova colisão. O guia de release passa a dizer que a documentação é publicada à parte, com
46
+ `pnpm deploy:docs`. A skill `build-opus-ui` passa a ensinar `placement` nos filtros e o
47
+ `ButtonGroup` nos footers modais.
48
+
49
+ ### Correções do histórico
50
+
51
+ Estas entradas publicadas descrevem algo diferente do que o código daquela versão fez. Elas não
52
+ foram reescritas; a correção fica registrada aqui.
53
+
54
+ - **17.2.1 nunca foi publicada.** O conteúdo daquela entrada — `renderMessageActions` nas mensagens
55
+ enviadas, `activity=""` e o `title` das tools MCP — saiu na 18.0.0.
56
+ - **17.0.0 e 17.2.0 mudaram a composição explícita dentro de `PageShell` sem aviso de quebra.** A
57
+ 17.0.0 passou a exigir `PageHeader` onde a 16.x exigia `PageIntro`, e a 17.2.0 voltou a exigir
58
+ `PageIntro`. Desde a 18.0.0, ambos são opcionais e a página exige um único heading principal.
59
+ - **18.1.0:** `Item size="sm"` reduz também o espaço entre regiões, para `0.625rem`, e não somente o
60
+ padding vertical. A mesma versão trocou o fechamento de Dialog e Drawer, que na 18.0.1 era um
61
+ `Button` `icon`, pelo `CloseButton` (`neutral`, `subtle`, forma de pílula e `icon-sm`), com
62
+ a nova prop `closeSize`; nenhum dos dois foi anunciado. Ela também removeu breakpoints responsivos
63
+ e mudou o footer padrão do `ActionFormDialog`; quem tinha testes visuais ou telas abaixo de
64
+ `64rem` precisa revisá-los.
65
+
66
+ ## 18.1.0 — 2026-09-15
67
+
68
+ A UI passa a assumir layout desktop fixo com largura mínima suportada de `64rem`. Breakpoints de
69
+ viewport e container queries isoladas saem dos componentes; grids, dialogs, drawers e campos
70
+ preservam o estado correspondente a essa largura. Consultas de acessibilidade e capacidade, limites
71
+ contra a viewport e overflow continuam ativos. `ActionFormDialog` e Presentations modais passam a
72
+ dimensionar o footer pelo conteúdo, com `Cancelar` em `ghost`; `footerDistribution="equal"`
73
+ permanece disponível como escolha explícita e usa cancelamento `outline`.
74
+
75
+ Filtros de list actions passam a declarar `placement: 'inline' | 'advanced' | 'external'`.
76
+ Filtros `external` continuam no estado navegável e no input, mas deixam a apresentação para o
77
+ consumidor; filtros avançados podem ser agrupados por `section`, distribuídos em até três colunas
78
+ com `advancedFilters.columns` e usam calendário nos campos de data. A propriedade `advanced`
79
+ continua aceita durante a migração e equivale a `placement: 'advanced'`.
80
+
81
+ Tools de IA e MCP passam a projetar também as labels dos Produtos de Dados relacionados, mantendo
82
+ os identificadores técnicos existentes. Hosts podem apresentar nomes legíveis sem perder a
83
+ rastreabilidade usada na execução e na auditoria.
84
+
85
+ Alert, Toast e Item passam a compartilhar a densidade compacta das superfícies horizontais:
86
+ `0.75rem` de padding no tamanho padrão e corpo produtivo de `0.875rem`. Cada componente declara o
87
+ corpo produtivo na própria raiz, sem alterar a tipografia de `html` ou `body`; folhas internas de
88
+ Alert e Item deixam de repetir a classe já estabelecida pela raiz do componente. `Item size="sm"`
89
+ reduz somente o padding vertical para `0.5rem`. Rótulos de formulário também usam explicitamente
90
+ o corpo produtivo; `text-xs` permanece reservado a metadados compactos, como rótulos de detalhes.
91
+
92
+ `ToggleGroupItem` preserva seu estado selecionado quando compõe um `TooltipTrigger` com `asChild`.
93
+ O tooltip continua disponível sem substituir os atributos que identificam e marcam visualmente o
94
+ item ativo.
95
+
96
+ `TabsList variant="line"` passa a desenhar uma linha-base por toda a largura disponível na
97
+ orientação horizontal, mantendo as abas compactas e o indicador ativo sobre a borda. Dentro de
98
+ `DrawerBody` ou `DialogBody`, a linha atravessa os gutters até as bordas sem deslocar os rótulos. Os itens usam
99
+ padding horizontal zero, padding vertical de `0.75rem` e gap de `1.25rem`.
100
+ A altura do `line` horizontal passa a acompanhar o conteúdo, permitindo que esse padding defina
101
+ a área interativa em vez de ficar contido pela altura da escala de controles. A orientação vertical
102
+ preserva seu padding anterior. `Field orientation="responsive"` continua aceito como alias do layout
103
+ desktop horizontal, sem reintroduzir breakpoints.
104
+
105
+ Campos de entrada passam a usar `bg-muted/30` no tema claro, preservando no escuro o contraste de
106
+ `bg-input/30`. O cabeçalho da `Table framed` compartilha essa superfície. O footer de `Dialog`
107
+ usa uma versão mais suave desse mesmo fundo. A moldura do modal fica transparente no light e sutil
108
+ no dark; o backdrop volta a escurecer a página sem aplicar blur.
109
+
110
+ `Button variant="outline"` passa a ter fundo transparente em repouso nos contextos neutral,
111
+ primary e danger. A borda, o texto semântico e o preenchimento de hover permanecem inalterados.
112
+
10
113
  ## 18.0.1 — 2026-09-13
11
114
 
12
115
  Controles somente com ícone que pertencem ao chrome da superfície, como voltar e fechar, usam o
@@ -60,6 +163,8 @@ a API imperativa, usam `outline`; a decisão principal continua `solid`.
60
163
 
61
164
  ## 17.2.1 — 2026-09-12
62
165
 
166
+ > Esta versão nunca foi publicada no npm; o conteúdo abaixo saiu na 18.0.0.
167
+
63
168
  O `Chat` passa a renderizar `renderMessageActions` também nas mensagens enviadas. Data, cópia e
64
169
  outras ações contextuais podem usar a mesma extensão discreta nos dois lados da conversa.
65
170
  `activity=""` oferece o indicador compacto, com somente os pontos centralizados e “Pensando…”
package/PROMOTED.md CHANGED
@@ -1,11 +1,10 @@
1
1
  # Enhancements promovidas ao Opus
2
2
 
3
3
  O registro CURADO da régua "o Opus cresce por reincidência". O fluxo vivo NÃO passa por
4
- edição manual deste arquivo: o agente no projeto aponta em `.opus/issues.jsonl` do repo
5
- dele (instrução na skill `implement-opus-change`; `type` **enhancement** ou **bug**)
6
- o Maestro colhe no boot da sessão e abre a **Issue no repo do Opus**
7
- (`github.com/softize-dev/opus`, dedup por hash + ledger), onde a triagem e a decisão
8
- acontecem (aberta = pendente, fechada = decidida). Enhancement aceita com **n ≥ 2**
4
+ edição manual deste arquivo: cada apontamento vira uma **Issue no repo do Opus**
5
+ (`github.com/softize-dev/opus`), `type` **enhancement** ou **bug**, onde a triagem e a
6
+ decisão acontecem (aberta = pendente, fechada = decidida). O pacote não coleta nada do
7
+ projeto consumidor por conta própria. Enhancement aceita com **n 2**
9
8
  ocorrências reais — comportamento se paga na reincidência; estilo e conveniência não.
10
9
  Bug (defeito NO Opus) aciona com n=1: vira fix + CHANGELOG, **não** entra neste ledger.
11
10
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Opus
2
2
 
3
- > **Status:** v0 — pacote único `@softize/opus` com subpath exports (core + adapters). Uma versão, sem semver por módulo.
3
+ > Pacote único `@softize/opus` com subpath exports (core + adapters). Uma versão, sem semver por módulo.
4
4
 
5
5
  Protocolo de actions de ponta a ponta para TypeScript. Declare uma vez — input, autorização, execução, auditoria, feedback — e deixe os adapters materializarem a action na interface, no cliente, no servidor e no log.
6
6
 
@@ -43,6 +43,7 @@ Um pacote (`@softize/opus`), uma versão. Cada categoria abaixo é um **subpath
43
43
  | `@softize/opus/testing` | — | Harness de teste de actions pela fronteira do contrato (experimental) |
44
44
  | `@softize/opus/dsl` | — | Expressões declarativas avaliadas em `load` e em consultas |
45
45
  | `@softize/opus/seed` | — | Declaração e binding de datasets verificáveis operados pela CLI |
46
+ | `@softize/opus/presentation` | — | Presentations portáteis: `definePresentation`, invocação e rotas |
46
47
  | `@softize/opus/vite` | — | Plugins Vite: runtime em modo design e auth de sessão fixa |
47
48
 
48
49
  Os drivers são resolvidos por subpath ESM (no estilo do Drizzle): `@softize/opus/server/fastify`, `@softize/opus/data/kysely`.
@@ -90,7 +91,7 @@ await app.listen({ port: 3000 })
90
91
  src/
91
92
  core/ schema/ server/ client/ ui/ data/ auth/ audit/
92
93
  log/ queue/ events/ scheduler/ storage/ cache/ ai/ mcp/
93
- observability/ testing/ dsl/ seed/ vite/
94
+ observability/ testing/ dsl/ seed/ vite/ presentation/
94
95
  registry/ # scaffolds + skills Opus — geração e conhecimento do SDK, não runtime
95
96
  bin/ # CLI (opus create/setup/gen/copy/check/db/seed/pre-push/introspect/mcp) + libs
96
97
  docs/
@@ -117,8 +118,8 @@ O contrato não oferece reset/truncate, e `apply` precisa convergir quando repet
117
118
 
118
119
  ## Status
119
120
 
120
- - **v0**: 21 superfícies, suíte vitest com cobertura medida por `pnpm test:cov`. O threshold de 100% em `vitest.config.ts` é aspiracional e não faz parte do gate de release (ver [docs/releasing.md](docs/releasing.md)).
121
- - **v1+**: drivers adicionais (Hono, Drizzle, ArkType, Vue, Inngest, Redis events), camada de entidades (`defineEntity`), Conversya migration plan.
121
+ - **Hoje**: 22 superfícies, suíte vitest com cobertura medida por `pnpm test:cov`. O threshold de 100% em `vitest.config.ts` é aspiracional e não faz parte do gate de release (ver [docs/releasing.md](docs/releasing.md)).
122
+ - **Próximos**: drivers adicionais (Hono, Drizzle, ArkType, Vue, Inngest, Redis events) e o plano de migração da Conversya.
122
123
 
123
124
  ## Desenvolvimento
124
125
 
package/bin/cli.mjs CHANGED
@@ -426,6 +426,10 @@ Regras da UI (@softize/opus/ui):
426
426
  unpaired-ui-surface — superfície sem o foreground do par (bg-card sem
427
427
  text-card-foreground, bg-popover sem text-popover-foreground)
428
428
 
429
+ Regras dos Produtos de Dados (defineDataProduct):
430
+ data-product-* — declaração exportada, id e versão válidos, sem duplicata,
431
+ interfaces e entidades que existem no domínio
432
+
429
433
  Exit ≠ 0 se houver violação. Sem nenhuma action: projeto marcado (opus.json) passa
430
434
  vacuamente; sem marcador, falha — gate vazio não é aprovação. Default dir: cwd.
431
435
  `)
@@ -1,5 +1,8 @@
1
1
  # ADR 0004 — O estado integral do conteúdo é composto dentro de Page
2
2
 
3
+ - **Status:** aceita, com adendo.
4
+ - **Data:** 2026-09-04.
5
+
3
6
  > **Atualização (2026-09-09).** A decisão original preservava o cabeçalho da página em todos os
4
7
  > estados. Ela foi invertida: `PageState` passa a ocultá-lo em `loading`, `error` e `empty`, e a
5
8
  > restaurá-lo em `ready`. A lista de responsabilidades abaixo já descreve o comportamento novo; o
@@ -5,10 +5,14 @@
5
5
 
6
6
  > **Atualização (2026-09-09).** Parcialmente substituída pela ADR 0009 quanto a `PageMeta` e ao
7
7
  > `count` de `Page`, e complementada pela ADR 0010, que acrescenta as apresentações `default` e
8
- > `bar` ao mesmo header. O corpo abaixo já traz a anatomia vigente.
8
+ > `bar` ao mesmo header.
9
9
  >
10
- > **Atualização (2026-09-11).** As ADRs 0012 e 0014 removem Description dos headers de Page,
10
+ > **Atualização (2026-09-11).** As ADRs 0018 e 0014 removem Description dos headers de Page,
11
11
  > Dialog e Drawer. Contexto relevante começa no body.
12
+ >
13
+ > **Atualização (2026-09-16).** O corpo abaixo descreve a anatomia da época em que foi escrito, não a
14
+ > vigente: a apresentação `bar` da ADR 0010 saiu com a ADR 0011, e nenhum header estrutural carrega
15
+ > mais descrição (ADRs 0018 e 0014). A anatomia atual está na documentação de cada família.
12
16
 
13
17
  ## Contexto
14
18
 
@@ -2,11 +2,16 @@
2
2
 
3
3
  - **Status:** aceita.
4
4
  - **Data:** 2026-09-10.
5
- - **Revisa:** ADR 0010.
5
+ - **Substitui:** ADR 0010.
6
6
 
7
7
  > **Atualização (2026-09-13).** A ADR 0016 separa o chrome persistente da introdução e do
8
8
  > cabeçalho de uma coleção. `PageHeader` passa a declarar navegação e ações globais para a barra;
9
9
  > `PageIntro` permanece opcional no conteúdo e `Content` nomeia coleções.
10
+ >
11
+ > **Atualização (2026-09-16).** Desde a 18.0.0, a composição explícita dentro do shell aceita
12
+ > `PageHeader`, `PageIntro` e `PageFooter` como opcionais e exige exatamente um heading principal —
13
+ > `PageTitle`, `Content level={1}` ou um `PageState` ativo. As frases abaixo que pedem `PageIntro`
14
+ > "sem `PageHeader`" descrevem a regra da 17.2 e não valem mais.
10
15
 
11
16
  ## Contexto
12
17
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  - **Status:** aceita.
4
4
  - **Data:** 2026-09-11.
5
- - **Complementa:** ADRs 0005, 0011 e 0012.
5
+ - **Complementa:** ADRs 0005, 0011 e 0018.
6
6
 
7
7
  ## Contexto
8
8
 
@@ -13,7 +13,7 @@ superfície e misturava o nome do recurso com instruções e consequências da t
13
13
  ## Decisão
14
14
 
15
15
  Headers estruturais contêm navegação opcional, título e actions. Page não oferece a propriedade
16
- `description` nem `PageDescription`; Dialog e Drawer seguem a mesma regra conforme a ADR 0012.
16
+ `description` nem `PageDescription`; Dialog e Drawer seguem a mesma regra conforme a ADR 0018.
17
17
 
18
18
  Contexto que altera compreensão ou decisão aparece no início do body. Ele pode ser texto, `Alert`,
19
19
  `Content` ou outra composição adequada. `PageState`, `Alert`, `Empty`, campos, métricas e seções
@@ -21,10 +21,11 @@ risco.
21
21
 
22
22
  - ações textuais que decidem a superfície usam `default`: header de Page e footer de Page, Dialog
23
23
  ou Drawer;
24
- - ações operacionais em toolbar, header de seção ou coleção densa usam `sm`;
24
+ - ações operacionais em toolbar, header de seção ou coleção densa usam `sm`; a criação no header
25
+ de uma coleção é a exceção e usa `default` (ADR 0016);
25
26
  - ações internas de linha, célula ou campo usam `xs` ou `icon-xs`;
26
- - ações somente com ícone que pertencem ao chrome da superfície, como voltar e fechar, usam
27
- `icon`;
27
+ - ações somente com ícone que pertencem ao chrome da superfície, como voltar, usam `icon`; desde a
28
+ 18.1.0 o fechamento de Dialog e Drawer é o `CloseButton`, em `icon-sm` por padrão (`closeSize`);
28
29
  - `lg` fica reservado a chamadas que deliberadamente precisam de uma área de toque maior, não a
29
30
  uma ação primária comum.
30
31
 
@@ -0,0 +1,54 @@
1
+ # ADR 0017 — A UI assume layout desktop fixo
2
+
3
+ - **Status:** aceita.
4
+ - **Data:** 2026-09-15.
5
+
6
+ ## Contexto
7
+
8
+ A UI acumulou adaptações isoladas por viewport em dialogs, drawers, campos, detalhes, paginação e
9
+ grids. O shell, as sidebars e os splits continuaram desktop-only. Essa combinação não formava um
10
+ contrato responsivo: abaixo da largura de trabalho, algumas partes mudavam de forma enquanto a
11
+ estrutura principal permanecia fixa.
12
+
13
+ ## Decisão
14
+
15
+ O Opus assume uma largura mínima suportada de `64rem`. Componentes usam um layout fixo equivalente
16
+ ao estado que os breakpoints `sm`, `md` e `lg` produziam nessa largura. Estados exclusivos de
17
+ `xl` e `2xl` não alteram mais a composição.
18
+
19
+ A UI viva não usa media queries de largura, variantes responsivas do Tailwind nem container queries.
20
+ Uma futura responsividade baseada no espaço do container exige uma decisão própria e uma aplicação
21
+ sistêmica; não permanece ativa de forma incidental em componentes isolados.
22
+
23
+ Consultas de preferência ou capacidade, como `prefers-reduced-motion`, `forced-colors` e
24
+ `hover`, continuam válidas. Overflow, limites de altura e posicionamento contra a viewport também
25
+ continuam protegendo conteúdo; essas medidas não prometem uma composição alternativa.
26
+
27
+ Footers de formulário modal dimensionam as ações pelo conteúdo e usam `Cancelar` em `ghost`.
28
+ `distribution="equal"` permanece uma escolha explícita para decisões de peso equivalente e,
29
+ nesse caso, o cancelamento usa `outline`.
30
+
31
+ ## Consequências
32
+
33
+ - O mesmo componente preserva a composição a partir de `64rem`, inclusive dentro de panes,
34
+ drawers e splits.
35
+ - Abaixo da largura suportada, compressão ou rolagem não constitui regressão do contrato atual.
36
+ - Dialogs mantêm limites fixos de largura; tabelas preservam rolagem horizontal.
37
+ - A remoção de breakpoints não remove adaptações de acessibilidade nem contenção contra overflow.
38
+ - Uma iniciativa responsiva futura começa pelo shell e define containers, navegação, tabelas e
39
+ testes de largura antes de adaptar componentes.
40
+
41
+ ## Alternativas descartadas
42
+
43
+ - **Manter adaptações pontuais:** conserva algum conforto em telas estreitas, mas continua sugerindo
44
+ um suporte que o shell não oferece.
45
+ - **Completar responsividade por viewport agora:** amplia a mudança para navegação, splits, tabelas
46
+ e fluxos que ainda não possuem requisitos de produto.
47
+ - **Preservar container queries isoladas:** usa uma técnica adequada para o futuro, mas mantém hoje
48
+ um contrato parcial sem cobertura sistêmica.
49
+
50
+ ## Verificação
51
+
52
+ - Um teste de inventário reprova variantes de largura e container queries na UI viva.
53
+ - Testes estruturais fixam layouts, larguras de modal e distribuição dos footers.
54
+ - Documentação pública declara a largura mínima, o default por conteúdo e a exceção `equal`.
@@ -1,4 +1,7 @@
1
- # ADR 0012 — O cabeçalho modal apenas nomeia a superfície
1
+ # ADR 0018 — O cabeçalho modal apenas nomeia a superfície
2
+
3
+ > Publicada originalmente com o número 0012, que colidia com a ADR de Produtos de Dados do mesmo
4
+ > dia. Renumerada em 2026-09-16; o conteúdo não mudou.
2
5
 
3
6
  - **Status:** aceita.
4
7
  - **Data:** 2026-09-11.
@@ -0,0 +1,65 @@
1
+ # ADR 0019 — Superfícies produtivas usam densidade compacta
2
+
3
+ > Publicada originalmente com o número 0016, que colidia com a ADR do cabeçalho da coleção.
4
+ > Renumerada em 2026-09-16; o conteúdo não mudou.
5
+
6
+ - **Status:** aceita.
7
+ - **Data:** 2026-09-14.
8
+
9
+ ## Contexto
10
+
11
+ Alert, Toast e Item compartilham uma composição horizontal de mídia, texto e ações, mas nasceram
12
+ com medidas internas diferentes ou herdadas de dependências. Alert e Item usavam `1rem` de padding;
13
+ o Toast preservava `1rem` e corpo de `0.8125rem` do Sonner. Consumidores já reconstruíam mensagens
14
+ parecidas com `0.75rem`, sinal de que a densidade da família não estava definida pela biblioteca.
15
+
16
+ A tipografia também repetia `text-sm` em raiz e folhas do mesmo componente. Essa repetição não
17
+ distinguia uma fronteira tipográfica de um detalhe interno e tornava difícil saber quando uma classe
18
+ era contrato ou apenas redundância.
19
+
20
+ ## Decisão
21
+
22
+ O tema não redefine a tipografia de `html`, `:root` ou `body`. Componentes que constituem uma
23
+ fronteira reutilizável declaram `text-sm`; folhas internas herdam essa medida, salvo quando
24
+ restauram o corpo produtivo dentro de um contexto tipográfico diferente.
25
+
26
+ Superfícies horizontais produtivas usam `0.75rem` de padding no tamanho padrão:
27
+
28
+ - Alert usa `p-3` e mantém mídia, texto e ações na mesma anatomia;
29
+ - Toast sobrescreve o padding e o corpo nativos do Sonner para repetir a medida do Alert;
30
+ - Item usa `p-3` e `gap-3`; `size="sm"` conserva `0.75rem` na horizontal e reduz a vertical para
31
+ `0.5rem`.
32
+
33
+ Mais espaço permanece deliberado quando o componente representa outra tarefa: Empty cria uma
34
+ pausa de estado na região disponível; Dialog e Drawer estruturam faixas e corpo; Card deixa o
35
+ padding sob responsabilidade dos seus slots ou do consumidor; conteúdo editorial usa `text-base`
36
+ para leitura prolongada.
37
+
38
+ ## Consequências
39
+
40
+ - Alert, Toast e Item passam a parecer membros da mesma família sem virar o mesmo componente.
41
+ - Texto solto preserva o tamanho do documento; uma fronteira reutilizável não depende
42
+ acidentalmente do tamanho escolhido pelo consumidor.
43
+ - `p-4` deixa de ser o default de mensagens e linhas, mas continua válido em contêineres
44
+ estruturais cujo papel exige mais respiro.
45
+ - A integração com Sonner precisa de teste de contrato porque seus defaults deixam de governar a
46
+ tipografia e o padding do Toast.
47
+
48
+ ## Alternativas descartadas
49
+
50
+ - **Reduzir a fonte raiz:** compactaria também espaços, dimensões, radius e elevação, misturando
51
+ densidade global com o papel do texto.
52
+ - **Criar aliases como `text-body` ou `surface-padding`:** esconderia os mesmos degraus sem
53
+ esclarecer onde a decisão pertence.
54
+ - **Remover toda ocorrência de `text-sm`:** faria componentes reutilizáveis dependerem do contexto
55
+ externo e impediria a restauração explícita do corpo produtivo.
56
+ - **Aplicar `p-3` a toda superfície:** reduziria modais, estados vazios e conteúdo editorial sem
57
+ relação com a composição horizontal que motivou a decisão.
58
+
59
+ ## Verificação
60
+
61
+ - testes estruturais conferem padding e herança em Alert e Item;
62
+ - o teste da integração com Sonner fixa padding, corpo e entrelinha do Toast;
63
+ - o contrato de unidades relativas confirma a ausência de tipografia global em `html`, `:root`
64
+ e `body`;
65
+ - documentação de Alert, Toast, Item e tokens classifica os defaults e as exceções.
@@ -98,8 +98,8 @@ Base instalada é dona do catálogo, dos kinds aceitos e do fundamento de cada r
98
98
  | `Select.options[].hint/triggerLabel/group` | `label`, `label`, `heading` |
99
99
  | `ActionTrigger.confirm.*` | papel correspondente do diálogo |
100
100
  | `t.dict` — `label`, `description`, `doc` das entradas e `doc` do dicionário | `label`, `description` |
101
- | `DataState`/`PageState`/`ActionList`/`ActionListDialog` — `emptyMessage`, `errorMessage`, `retryLabel`; `PageState`/`ActionListDialog` — `title`, `description`; `ActionView.emptyMessage` | `empty-state`, `error`, `button`, `title`, `description` |
102
- | `dialog.alert/confirm/prompt/choose()` — `title`, `description`, `body`, `action`, `cancel`, `placeholder`, `actions[].label` | papel correspondente do diálogo |
101
+ | `DataState`/`PageState`/`ActionList`/`ActionListDialog` — `emptyMessage`, `errorMessage`, `retryLabel`; `PageState` — `title`, `description`; `ActionListDialog` — `title`, `intro`; `ActionView.emptyMessage` | `empty-state`, `error`, `button`, `title`, `description` |
102
+ | `dialog.alert/confirm/prompt/choose()` — `title`, `body`, `action`, `cancel`, `placeholder`, `actions[].label` | papel correspondente do diálogo |
103
103
  | `TooltipContent` (children), `LabelHelp.help` | `label`, `helper-text` |
104
104
 
105
105
  ## Cobertura e significado do gate verde
@@ -52,6 +52,6 @@ Há três sinais diferentes e eles não devem ser confundidos:
52
52
 
53
53
  ## Verificação
54
54
 
55
- - o status do Maestro deve distinguir versão instalada, aplicada e adotada;
55
+ - o status do Maestro deve distinguir versão disponível, adotada e aplicada;
56
56
  - fixtures com tokens removidos devem reprovar no detector de migração, enquanto texto de
57
57
  documentação e dependências geradas são ignorados.
@@ -58,9 +58,11 @@ consumidores usam o nome qualificado; o alias só poderá ser removido numa vers
58
58
  ## Projeções
59
59
 
60
60
  `opus gen` publica os produtos no `.opus/manifest.json`. Actions expostas como tools carregam os
61
- IDs em `metadata.dataProducts`; no MCP, a chave é
62
- `_meta['com.softize.opus/data-products']`. Consumidores devem registrar apenas produtos associados
63
- a chamadas concluídas com sucesso.
61
+ IDs em `metadata.dataProducts` e os rótulos legíveis em `metadata.dataProductLabels`, um mapa de ID
62
+ para `label`. No MCP, as chaves são `_meta['com.softize.opus/data-products']` e
63
+ `_meta['com.softize.opus/data-product-labels']`. Hosts mostram o rótulo às pessoas e usam o ID na
64
+ execução e na auditoria. Consumidores devem registrar apenas produtos associados a chamadas
65
+ concluídas com sucesso.
64
66
 
65
67
  O Opus Lens usa o manifest para mostrar a linhagem declarada entre Fontes, entities, produtos e
66
68
  Actions. Nenhuma dessas projeções é fonte autoritativa: corrija a declaração e gere novamente.
package/docs/protocol.md CHANGED
@@ -604,7 +604,7 @@ Não vai ter `compose(authA, authB)` no core. Se sentir falta, é regra de negó
604
604
 
605
605
  O Opus **não autentica** — não valida token, não emite sessão. O adapter (server) recebe a request, valida o token via auth provider externo (Better Auth, Clerk, próprio), monta `ctx.user`, e entrega pro Opus. A partir daí, `authorize` decide.
606
606
 
607
- Se `ctx.user` é `null` e `public !== true`, runtime rejeita com `{ code: 'auth.unauthenticated', category: 'authentication' }` **antes** de chamar `authorize`.
607
+ Se `ctx.user` é `null` e `public !== true`, runtime rejeita com `{ code: 'auth.unauthenticated', category: 'authentication' }` **antes** dos loaders e de `authorize`. Assim um chamador anônimo não dispara consultas nem descobre, pelo erro de um loader, se um registro existe.
608
608
 
609
609
  ---
610
610
 
@@ -1186,9 +1186,9 @@ ctx = { user, tenantId, can, db, emit, meta, ... }
1186
1186
 
1187
1187
  Runtime.execute(action, input, ctx)
1188
1188
  ↓ (1) validate input contra schema
1189
- ↓ (2) load (se action declara `loads`)
1190
- ↓ (3) check public OR authenticate (ctx.user != null)
1191
- ↓ (4) authorize (chama action.authorize)
1189
+ ↓ (2) check public OR authenticate (ctx.user != null)
1190
+ ↓ (3) load (se action declara `loads`)
1191
+ ↓ (4) authorize (chama action.authorize, que pode usar o que foi carregado)
1192
1192
  ↓ (5) handler executa
1193
1193
  (durante: handler pode chamar ctx.emit(event, data)
1194
1194
  → runtime publica via EventBusAdapter)
@@ -1426,14 +1426,14 @@ type BackgroundResult<T> =
1426
1426
  ```
1427
1427
  HTTP request → Server adapter
1428
1428
  ↓ (1) validate input
1429
- ↓ (2) load + authenticate + authorize
1429
+ ↓ (2) authenticate + load + authorize
1430
1430
  ↓ (3) enqueue job via QueueAdapter + audit do enqueue
1431
1431
 
1432
1432
  Server retorna JobHandle síncrono → client polla ou subscribe
1433
1433
 
1434
1434
  Worker (processo separado) consome job:
1435
1435
  ↓ (a) reidrata auth/can e chama runtime.executeJob(spec, ctx, progress)
1436
- ↓ (b) revalida input + load + authenticate + authorize
1436
+ ↓ (b) revalida input + authenticate + load + authorize
1437
1437
  ↓ (c) handler (recebe ProgressReporter se config.progress)
1438
1438
  ↓ (d) validate output + audit com provenance background
1439
1439
  ↓ (e) processor retorna/lança; BullMQ atualiza status done/failed
@@ -15,12 +15,17 @@ compacto deixavam de acompanhar a raiz mesmo quando a aplicação assumia essa r
15
15
 
16
16
  ## Decisão
17
17
 
18
- - O tema do Opus não declara `font-size` em `html` nem em `:root`. O navegador e a aplicação
19
- definem a fonte raiz.
18
+ - O tema do Opus não declara `font-size` em `html`, `:root` nem `body`. O navegador e a aplicação
19
+ definem a fonte raiz e o texto geral do documento.
20
20
  - Medidas que pertencem à identidade e à escala da interface usam `rem`, diretamente ou pela
21
21
  escala do Tailwind, que também é relativa à raiz.
22
22
  - Aplicações ajustam a densidade no próprio CSS. Para conservar a proporção que antes resultava
23
23
  de uma raiz de 15 sobre a base usual de 16, podem usar `html { font-size: 93.75%; }`.
24
+ - Ajustar a raiz é uma decisão de escala da interface inteira, não uma forma de renomear papéis
25
+ tipográficos. Na escala produtiva recomendada, `text-sm` (`0.875rem`) é o corpo comum da
26
+ interface e deve ser declarado na raiz controlada do componente ou da região. `text-base`
27
+ (`1rem`) fica reservado à leitura mais confortável; a documentação de tokens apresenta a regra
28
+ de composição e herança.
24
29
  - `px` fica restrito a encaixes técnicos que não devem crescer com a tipografia: espessura de
25
30
  hairline, compensação exata de uma borda, ajuste óptico preso a esse traço e o raio efetivamente
26
31
  infinito do scrollbar nativo. Cada ocorrência integra a allowlist do teste de contrato.
@@ -31,6 +36,8 @@ compacto deixavam de acompanhar a raiz mesmo quando a aplicação assumia essa r
31
36
 
32
37
  - Sem uma regra da aplicação, `1rem` segue a configuração do navegador. A UI pode ficar maior
33
38
  para consumidores que recebiam os antigos 15px; essa mudança é visual e intencional.
39
+ - Texto solto, fora de uma raiz que declare seu papel tipográfico, permanece no tamanho geral do
40
+ documento; componentes produtivos não dependem de uma redução global para chegar a `0.875rem`.
34
41
  - Preferências de acessibilidade e identidades próprias deixam de disputar com um override da
35
42
  biblioteca.
36
43
  - Alterar a raiz na aplicação escala texto, espaçamento, radius, foco, elevação e limites
package/docs/releasing.md CHANGED
@@ -13,6 +13,13 @@ configuração de escopo ou token de leitura.
13
13
 
14
14
  ## Release pela estação (o caminho normal)
15
15
 
16
+ Antes de rodar, escreva no `CHANGELOG.md` a entrada `## X.Y.Z` da versão que vai sair. O script
17
+ confere essa entrada só DEPOIS do bump, então esquecê-la aborta a release com o `package.json` já
18
+ alterado — e a tentativa seguinte para na guarda de árvore limpa. Para recuperar, desfaça o bump
19
+ (`git checkout -- package.json`), escreva a entrada, commite e rode de novo.
20
+
21
+ Os comandos rodam em `packages/opus`; o `package.json` da raiz não tem `release`.
22
+
16
23
  ```bash
17
24
  pnpm release # patch bump + publica no npm público
18
25
  pnpm release minor # minor
@@ -53,7 +60,7 @@ pra testar publicação seria a guarda atrapalhando quem está experimentando.
53
60
 
54
61
  **Gate de qualidade**: depois do bump e da materialização, mas antes de publicar, roda
55
62
  `pnpm typecheck` + `pnpm test` + `pnpm copy:check` + `base copy check` e **aborta a release
56
- se qualquer um falhar**. O Opus declara `@softize/base ^2.2.1` em `dependencies`, pois usa
63
+ se qualquer um falhar**. O Opus declara `@softize/base ^2.3.0` em `dependencies`, pois usa
57
64
  suas APIs públicas de filesystem em runtime, e materializa os artefatos Base no próprio repo;
58
65
  a release não baixa uma política ad hoc. Como o
59
66
  Opus **ship source** (`.ts`, sem build), essa é a última barreira antes do tarball — sem ela,
@@ -67,7 +74,7 @@ não é bloqueada por cobertura abaixo de 100%; a cobertura é acompanhada com `
67
74
  **Smoke do esqueleto**: depois do bump e antes do publish, o `release.sh` gera um app com
68
75
  `opus create`, instala o **tarball exato** que vai ser publicado e roda os gates dele
69
76
  (typecheck · test · `opus check` · `opus copy --check` · `base copy check` · manifest ·
70
- build). O template exige `@softize/base ^2.2.1`; publique a Base compatível antes do Opus.
77
+ build). O template exige `@softize/base ^2.3.0`; publique a Base compatível antes do Opus.
71
78
  O `minimumReleaseAgeExclude` do template inclui os dois pacotes, e o smoke executa os
72
79
  fragmentos de pre-push para provar que o layout pnpm instalado resolve ambos os CLIs.
73
80
  É o que pega o que typecheck+test não
@@ -80,7 +87,24 @@ O script bumpa, publica **e leva o bump pra `origin/main`** — não sobra passo
80
87
  `--local` ele não commita nada (Verdaccio é sandbox). `pnpm release none` não cria diff de
81
88
  versão, mas ainda commita qualquer derivado que a materialização legitimamente atualizar.
82
89
  `--dry-run` encerra depois das mesmas materializações/gates/smoke, sem publish, commit ou
83
- push; use `none` para validar a versão atual sem sujar o manifesto.
90
+ push; use `none` para validar a versão atual sem sujar o manifesto. Sem `--local`, o
91
+ `--dry-run` também exige a `main` limpa e igual a `origin/main`.
92
+
93
+ ## A documentação é publicada à parte
94
+
95
+ A release publica o pacote, não o site. `opus.softize.com.br` só muda quando alguém roda, na raiz
96
+ do repositório:
97
+
98
+ ```bash
99
+ pnpm deploy:docs
100
+ ```
101
+
102
+ O script aplica as mesmas guardas de procedência da release, roda `typecheck`, `test`,
103
+ `copy:check` e o `opus check` de `apps/opus`, builda o site e envia o resultado. Ele não roda o
104
+ `base copy check` nem o smoke do esqueleto, que ficam com a release. Rode-o depois de cada
105
+ release; sem isso, o site continua mostrando a versão anterior.
106
+ Em setembro de 2026, o site ficou onze dias preso numa versão anterior à 13.0.0 enquanto o
107
+ pacote chegava à 18.1.0. `pnpm deploy:docs --dry-run` valida sem enviar.
84
108
 
85
109
  ## Verificar
86
110
 
@@ -111,7 +135,7 @@ pnpm add @softize/opus@8.7.0-rc.0 --registry http://127.0.0.1:6873/
111
135
 
112
136
  Valide o app normalmente. Ao encerrar a sessão, restaure o intervalo de versão do
113
137
  consumidor para o npm e rode `pnpm install`; a prerelease local nunca é enviada ao npm.
114
- Para publicar outra tentativa, incremente o sucesso (`8.6.0-rc.1`, por exemplo) e repita
138
+ Para publicar outra tentativa, incremente o sufixo (`8.7.0-rc.1`, por exemplo) e repita
115
139
  o mesmo fluxo.
116
140
 
117
141
  ## Auth
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softize/opus",
3
- "version": "18.0.1",
3
+ "version": "18.1.1",
4
4
  "description": "End-to-end action protocol for TypeScript. Single package with subpath exports (core + adapters).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,7 +12,9 @@
12
12
  `ActionFilterBar` separado do renderer, `ItemGroup` para
13
13
  uma coleção secundária e um `ActionFormDialog`; provar teto padrão de `80rem`, hierarquia por
14
14
  `level`, vazio estrutural sólido, `Empty` apenas para criação ou vínculo, números sem alinhamento
15
- inferido e cancelamento `outline` no footer 50/50 do modal.
15
+ inferido e footer modal com largura natural, alinhado à direita e cancelamento `ghost`; incluir
16
+ separadamente um caso deliberadamente equivalente com distribuição 50/50 e cancelamento
17
+ `outline` explícitos.
16
18
  - Execução roteável: declarar Page, criação e edição com `Presentation.route`; provar deep link,
17
19
  hidratação do registro, fechamento para a lista e projeção integral no manifest.
18
20
  - Execução abreviada: montar outra página com `<Page title actions>` e uma seção com