@softize/opus 18.1.0 → 18.2.0

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 (102) hide show
  1. package/CHANGELOG.md +75 -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/{0012-modal-header-only-names-the-surface.md → 0018-modal-header-only-names-the-surface.md} +4 -1
  11. package/docs/adr/{0016-productive-surfaces-use-compact-density.md → 0019-productive-surfaces-use-compact-density.md} +4 -1
  12. package/docs/code-style.md +2 -2
  13. package/docs/consumer-upgrade-propagation.md +1 -1
  14. package/docs/data-products.md +5 -3
  15. package/docs/protocol.md +6 -6
  16. package/docs/releasing.md +28 -4
  17. package/package.json +1 -1
  18. package/registry/skills/build-opus-ui/references/ui-patterns.md +11 -1
  19. package/src/auth/drivers/jwt.ts +2 -1
  20. package/src/core/presentation.ts +143 -58
  21. package/src/core/runtime.ts +25 -4
  22. package/src/core/types.ts +4 -4
  23. package/src/mcp/index.ts +9 -0
  24. package/src/ui/components/patterns/content-header.tsx +1 -1
  25. package/src/ui/components/patterns/form.tsx +1 -1
  26. package/src/ui/components/patterns/presentation.tsx +199 -60
  27. package/src/ui/components/patterns/sidebar.tsx +1 -1
  28. package/src/ui/components/patterns/split.tsx +5 -2
  29. package/src/ui/components/patterns/surface-assistant.tsx +74 -0
  30. package/src/ui/components/primitives/card.tsx +1 -1
  31. package/src/ui/components/primitives/detail.tsx +7 -7
  32. package/src/ui/components/primitives/radio-group.tsx +1 -1
  33. package/src/ui/components/primitives/select.tsx +1 -1
  34. package/src/ui/docs/content/action-form-dialog.md +11 -4
  35. package/src/ui/docs/content/action-form.md +7 -16
  36. package/src/ui/docs/content/action-list-dialog.md +4 -6
  37. package/src/ui/docs/content/action-list.md +46 -4
  38. package/src/ui/docs/content/action-trigger.md +9 -5
  39. package/src/ui/docs/content/action-view.md +12 -8
  40. package/src/ui/docs/content/actions.md +36 -13
  41. package/src/ui/docs/content/ai.md +26 -7
  42. package/src/ui/docs/content/alert.md +4 -3
  43. package/src/ui/docs/content/aspect-ratio.md +2 -2
  44. package/src/ui/docs/content/auth.md +25 -10
  45. package/src/ui/docs/content/avatar.md +1 -1
  46. package/src/ui/docs/content/badge.md +2 -2
  47. package/src/ui/docs/content/breadcrumb.md +3 -2
  48. package/src/ui/docs/content/button.md +31 -7
  49. package/src/ui/docs/content/calendar.md +1 -1
  50. package/src/ui/docs/content/card.md +1 -1
  51. package/src/ui/docs/content/carousel.md +14 -3
  52. package/src/ui/docs/content/chat.md +1 -1
  53. package/src/ui/docs/content/cli.md +13 -7
  54. package/src/ui/docs/content/command.md +34 -2
  55. package/src/ui/docs/content/composer.md +1 -1
  56. package/src/ui/docs/content/content.md +5 -4
  57. package/src/ui/docs/content/customization.md +1 -1
  58. package/src/ui/docs/content/cycle.md +7 -5
  59. package/src/ui/docs/content/data-state.md +6 -5
  60. package/src/ui/docs/content/data.md +3 -3
  61. package/src/ui/docs/content/detail.md +3 -2
  62. package/src/ui/docs/content/dialog.md +2 -2
  63. package/src/ui/docs/content/dictionary-value.md +1 -1
  64. package/src/ui/docs/content/dock.md +23 -2
  65. package/src/ui/docs/content/dot.md +0 -2
  66. package/src/ui/docs/content/drawer.md +1 -1
  67. package/src/ui/docs/content/empty.md +1 -4
  68. package/src/ui/docs/content/events.md +1 -1
  69. package/src/ui/docs/content/field.md +20 -11
  70. package/src/ui/docs/content/getting-started.md +4 -2
  71. package/src/ui/docs/content/icon-picker.md +2 -2
  72. package/src/ui/docs/content/input-otp.md +2 -0
  73. package/src/ui/docs/content/input.md +2 -3
  74. package/src/ui/docs/content/item.md +5 -4
  75. package/src/ui/docs/content/kbd.md +2 -1
  76. package/src/ui/docs/content/mcp.md +10 -4
  77. package/src/ui/docs/content/menu.md +27 -0
  78. package/src/ui/docs/content/page.md +19 -5
  79. package/src/ui/docs/content/pagination.md +9 -2
  80. package/src/ui/docs/content/popover.md +2 -2
  81. package/src/ui/docs/content/presentation.md +85 -58
  82. package/src/ui/docs/content/progress.md +2 -6
  83. package/src/ui/docs/content/runtime.md +8 -5
  84. package/src/ui/docs/content/scheduler.md +1 -1
  85. package/src/ui/docs/content/select.md +13 -8
  86. package/src/ui/docs/content/sidebar.md +3 -2
  87. package/src/ui/docs/content/skeleton.md +1 -1
  88. package/src/ui/docs/content/slider.md +4 -4
  89. package/src/ui/docs/content/spinner.md +3 -3
  90. package/src/ui/docs/content/split.md +80 -19
  91. package/src/ui/docs/content/tabs.md +6 -6
  92. package/src/ui/docs/content/testing.md +4 -2
  93. package/src/ui/docs/content/toast.md +3 -5
  94. package/src/ui/docs/content/toggle.md +37 -0
  95. package/src/ui/docs/content/tooltip.md +4 -3
  96. package/src/ui/docs/content/truncate.md +3 -2
  97. package/src/ui/docs/content/ui.md +3 -1
  98. package/src/ui/docs/content/upgrading.md +43 -13
  99. package/src/ui/docs/doc-client.tsx +1 -1
  100. package/src/ui/docs/registry.tsx +30 -5
  101. package/src/ui/meta.ts +5 -5
  102. package/src/ui/react.tsx +5 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,79 @@ 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.2.0 — 2026-09-16
11
+
12
+ `Presentation` passa a aceitar um body serializável baseado em componente. O contrato continua
13
+ definindo rota, superfície, navegação e ações, enquanto a aplicação fornece o conteúdo React em
14
+ tempo de execução. Assim, uma página pode adotar a casca dirigida por spec sem precisar migrar de
15
+ uma vez toda a implementação interna para actions declarativas.
16
+
17
+ Recursos podem declarar um gatilho de assistência contextual em `assistant`. A aplicação fornece o
18
+ painel e os dados vivos do recurso por `assistant.render`; identidade, autorização e demais dados
19
+ não entram no manifest. O novo `SurfaceAssistant`, também disponível pela API pública, divide
20
+ somente o body de Page, Dialog ou Drawer. Cabeçalho e rodapé permanecem em toda a largura, e o
21
+ gatilho sai do cabeçalho enquanto o painel aberto assume a identidade visual da assistência.
22
+
23
+ O split contextual usa um único separador redimensionável, sem sobrepor uma segunda borda entre o
24
+ conteúdo e o painel. O conteúdo principal permanece montado durante a conversa e volta a ocupar
25
+ toda a largura quando ela é fechada.
26
+
27
+ ## 18.1.1 — 2026-09-16
28
+
29
+ **Correção de segurança.** Quem executa tools de IA só roda o que `aiTools()` anunciou. Até aqui, o
30
+ servidor MCP executava em `CallTool` qualquer action registrada, e o agente co-locado executava
31
+ qualquer nome de tool que o modelo emitisse. Um cliente MCP, um modelo ou um prompt injetado podia
32
+ rodar uma action sem `ai.enabled` — inclusive destrutiva — dentro da autorização do contexto
33
+ resolvido. Os dois caminhos agora recusam o nome fora do conjunto exposto, com a mesma resposta
34
+ para action inexistente e para action registrada mas não exposta, sem revelar quais existem.
35
+ Nenhuma mudança é necessária em quem já marcava com `ai.enabled` as actions que expõe; quem
36
+ dependia de chamar pelo MCP uma action não marcada precisa marcá-la.
37
+
38
+ Uma action que não é `public` passa a exigir usuário ANTES de rodar os loaders. Antes, os
39
+ `loads` consultavam dados para um chamador anônimo, que recebia o erro do loader — um
40
+ `not_found`, por exemplo — e podia usar essa diferença para descobrir se um registro existe. O
41
+ `authorize` continua depois dos loaders, porque depende do que eles carregam.
42
+
43
+ `RadioGroupItem` passa a ter a classe `peer`, como `Checkbox` e `Switch`: o `Label` associado
44
+ esmaece quando o item está desabilitado, como a documentação já descrevia. Em `DetailGroup` com
45
+ moldura e orientação horizontal, a largura padrão da coluna de rótulos passa a ser uma classe, e
46
+ uma classe do consumidor (`[--detail-label-width:9rem]`) volta a substituí-la; antes, só `style`
47
+ conseguia.
48
+
49
+ A documentação publicada foi revisada por inteiro, e as 92 páginas foram abertas num navegador
50
+ headless sem erro de console nem de preview. O exemplo principal de Presentation voltou a
51
+ renderizar: ele usava funções que não estavam no escopo dos previews e lançava `ReferenceError`.
52
+ Um exemplo do `IconPicker` declarava `Icon`, nome que o escopo já fornece, e mostrava um erro de
53
+ sintaxe no lugar do preview. Um gate novo compila cada preview com o escopo da sua página, do jeito
54
+ que o site o executa, e reprova nome ausente ou redeclarado; o anterior só conferia tags JSX. Sessenta e quatro páginas foram
55
+ corrigidas contra o código: props, defaults e tipos que não batiam, exemplos que não compilavam ou ensinavam padrões
56
+ substituídos pelas versões 16 a 18, e afirmações que o código não sustenta. Entre elas, `auth.md`
57
+ deixa de sugerir que o runtime aplica um campo `auth` inexistente, e `upgrading.md` passa a incluir
58
+ `pnpm run setup` e os gates de copy, sem os quais o fluxo de upgrade falhava no primeiro gate.
59
+
60
+ `opus check --help` lista as regras de Produto de Dados. As ADRs que dividiam número foram
61
+ renumeradas: a do cabeçalho modal passa a 0018 e a da densidade compacta, a 0019; um gate impede
62
+ nova colisão. O guia de release passa a dizer que a documentação é publicada à parte, com
63
+ `pnpm deploy:docs`. A skill `build-opus-ui` passa a ensinar `placement` nos filtros e o
64
+ `ButtonGroup` nos footers modais.
65
+
66
+ ### Correções do histórico
67
+
68
+ Estas entradas publicadas descrevem algo diferente do que o código daquela versão fez. Elas não
69
+ foram reescritas; a correção fica registrada aqui.
70
+
71
+ - **17.2.1 nunca foi publicada.** O conteúdo daquela entrada — `renderMessageActions` nas mensagens
72
+ enviadas, `activity=""` e o `title` das tools MCP — saiu na 18.0.0.
73
+ - **17.0.0 e 17.2.0 mudaram a composição explícita dentro de `PageShell` sem aviso de quebra.** A
74
+ 17.0.0 passou a exigir `PageHeader` onde a 16.x exigia `PageIntro`, e a 17.2.0 voltou a exigir
75
+ `PageIntro`. Desde a 18.0.0, ambos são opcionais e a página exige um único heading principal.
76
+ - **18.1.0:** `Item size="sm"` reduz também o espaço entre regiões, para `0.625rem`, e não somente o
77
+ padding vertical. A mesma versão trocou o fechamento de Dialog e Drawer, que na 18.0.1 era um
78
+ `Button` `icon`, pelo `CloseButton` (`neutral`, `subtle`, forma de pílula e `icon-sm`), com
79
+ a nova prop `closeSize`; nenhum dos dois foi anunciado. Ela também removeu breakpoints responsivos
80
+ e mudou o footer padrão do `ActionFormDialog`; quem tinha testes visuais ou telas abaixo de
81
+ `64rem` precisa revisá-los.
82
+
10
83
  ## 18.1.0 — 2026-09-15
11
84
 
12
85
  A UI passa a assumir layout desktop fixo com largura mínima suportada de `64rem`. Breakpoints de
@@ -107,6 +180,8 @@ a API imperativa, usam `outline`; a decisão principal continua `solid`.
107
180
 
108
181
  ## 17.2.1 — 2026-09-12
109
182
 
183
+ > Esta versão nunca foi publicada no npm; o conteúdo abaixo saiu na 18.0.0.
184
+
110
185
  O `Chat` passa a renderizar `renderMessageActions` também nas mensagens enviadas. Data, cópia e
111
186
  outras ações contextuais podem usar a mesma extensão discreta nos dois lados da conversa.
112
187
  `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
 
@@ -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.
@@ -1,4 +1,7 @@
1
- # ADR 0016 — Superfícies produtivas usam densidade compacta
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.
2
5
 
3
6
  - **Status:** aceita.
4
7
  - **Data:** 2026-09-14.
@@ -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
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.1.0",
3
+ "version": "18.2.0",
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",
@@ -73,6 +73,11 @@ ContentDescription, ContentMeta, ContentActions) + ContentBody`. `ContentHeader`
73
73
  - `ActionFilterBar` renderiza busca, filtros, período e ações declarados por uma list action
74
74
  quando a tela precisa da toolbar sem entregar os resultados a `ActionList`. Manter seu
75
75
  `state` e `onStateChange` ligados à mesma projeção de URL usada pela superfície.
76
+ - Filtros de list action declaram onde aparecem com `placement`: `inline` fica na barra;
77
+ `advanced` vai para o painel de filtros avançados, agrupado por `section` e distribuído em até
78
+ três colunas com `advancedFilters.columns`; `external` continua no estado navegável e no input,
79
+ mas a tela apresenta o controle por conta própria. `advanced: true` segue aceito só durante a
80
+ migração; em código novo, declarar `placement`.
76
81
  - `ActionForm` mantém validação, execução e estados do contrato nos dois modos: sem `children`,
77
82
  renderiza os campos declarados; com `children`, o consumidor diagrama `ActionFormField` e
78
83
  controles customizados pelo contexto. `ActionFormCard` e `ActionFormDialog` acrescentam a
@@ -81,8 +86,13 @@ ContentDescription, ContentMeta, ContentActions) + ContentBody`. `ContentHeader`
81
86
  a ação principal mantém `solid`. Criação e edição comuns preservam o rótulo padrão `Salvar`;
82
87
  `submitLabel` fica para efeitos específicos, como `Renomear` ou `Criar nova versão`. Em
83
88
  `Dialog mode="alert"`, a saída segura também usa `outline`.
89
+ - `DialogFooter` e `DrawerFooter` cuidam só da faixa. Agrupar e distribuir as decisões cabe a um
90
+ `ButtonGroup` dentro deles, com `distribution="equal"` para o footer 50/50; botões soltos no
91
+ footer não são distribuídos. `ActionFormDialog` já compõe isso: por padrão o footer acompanha o
92
+ conteúdo com o cancelamento em `ghost`, e `footerDistribution="equal"` divide a faixa e passa o
93
+ cancelamento a `outline`.
84
94
  - Cabeçalhos de `Dialog` e `Drawer` nomeiam a superfície com o título e organizam suas ações. O close
85
- padrão é uma action ghost somente com ícone no final do header; não crie uma posição flutuante
95
+ padrão é o `CloseButton` (`neutral`, `subtle`, pílula) no final do header, sem posição flutuante
86
96
  alternativa. Não preencher uma segunda linha por hábito. Consequência, restrição ou instrução que
87
97
  realmente mude a tarefa entra no início de `DialogBody`/`DrawerBody`, como texto ou `Alert`;
88
98
  wrappers usam `intro` e a API imperativa usa `body`. Relacione texto conciso por
@@ -34,7 +34,8 @@ import type { AuthAdapter, CanFn, User } from '../../core/index.ts'
34
34
  // =============================================================================
35
35
 
36
36
  export interface JwtAuthOptions<P extends JwtPayload = JwtPayload> {
37
- /** Secret HMAC ou public key. Pode ser string sync ou async resolver. */
37
+ /** Secret HMAC ou public key, fixo ou por resolver (sync ou async). O resolver é chamado
38
+ * sem argumentos: a escolha da chave pelo `kid` do token ainda não é suportada. */
38
39
  secret: string | Buffer | ((kid?: string) => Promise<string | Buffer> | string | Buffer)
39
40
 
40
41
  /** Algorithms aceitos (default ['HS256']). */