@softize/opus 12.6.3 → 12.7.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 (34) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/README.md +20 -2
  3. package/bin/cli.mjs +10 -1
  4. package/bin/lib/gen-manifest.mjs +1 -0
  5. package/bin/lib/gen-runner.mjs +29 -1
  6. package/bin/lib/seed-runner.mjs +152 -0
  7. package/bin/lib/seed.mjs +229 -0
  8. package/docs/adr/0002-structured-seeds-are-declared-and-bound.md +130 -0
  9. package/docs/protocol.md +10 -1
  10. package/docs/seeds.md +132 -0
  11. package/package.json +5 -1
  12. package/registry/instructions/opus.md +4 -0
  13. package/registry/skills/apply-opus-seed/SKILL.md +45 -0
  14. package/registry/skills/apply-opus-seed/agents/openai.yaml +4 -0
  15. package/registry/skills/apply-opus-seed/references/evaluations.md +8 -0
  16. package/registry/skills/create-opus-seed/SKILL.md +55 -0
  17. package/registry/skills/create-opus-seed/agents/openai.yaml +4 -0
  18. package/registry/skills/create-opus-seed/references/contract.md +16 -0
  19. package/registry/skills/create-opus-seed/references/evaluations.md +8 -0
  20. package/registry/skills/create-opus-seed/scripts/scaffold.mjs +78 -0
  21. package/registry/skills/implement-opus-change/SKILL.md +3 -1
  22. package/src/core/index.ts +2 -0
  23. package/src/core/types.ts +16 -1
  24. package/src/seed/index.ts +391 -0
  25. package/src/ui/components/patterns/form.tsx +131 -19
  26. package/src/ui/components/primitives/detail.tsx +113 -0
  27. package/src/ui/docs/content/action-form.md +10 -0
  28. package/src/ui/docs/content/cli.md +17 -0
  29. package/src/ui/docs/content/detail.md +38 -0
  30. package/src/ui/docs/registry.tsx +2 -0
  31. package/src/ui/drivers/react.tsx +3 -2
  32. package/src/ui/lib/object-schema.ts +36 -0
  33. package/src/ui/meta.ts +7 -1
  34. package/src/ui/react.tsx +9 -0
@@ -0,0 +1,130 @@
1
+ # ADR 0002 — Seeds estruturados separam declaração, execução e operação
2
+
3
+ - Status: aceita
4
+ - Data: 2026-08-25
5
+
6
+ ## Contexto e forças
7
+
8
+ Projetos consumidores criam dados de desenvolvimento por comandos, scripts e convenções locais.
9
+ O mesmo nome “seed” termina cobrindo usuários de desenvolvimento, demonstrações, datasets e
10
+ fixtures de teste, sem uma forma comum de descobrir dependências, perfis, proteção de ambiente ou
11
+ resultado esperado. Skills conseguem melhorar a experiência humana, mas não substituem uma
12
+ operação determinística que CI e automações possam executar sem um agente.
13
+
14
+ O contrato precisa:
15
+
16
+ - manter dados, regras de negócio, conexão e drivers sob responsabilidade do consumidor;
17
+ - oferecer descoberta e execução reproduzível pela CLI;
18
+ - separar metadata compartilhável de implementação server-only;
19
+ - bloquear produção e exigir um escopo de dados explícito antes de qualquer escrita;
20
+ - tornar dependências, perfis e resultados verificáveis;
21
+ - exigir aplicação convergente, sem transformar `apply` em reset implícito;
22
+ - materializar orientação para autoria e operação sem guardar SQL dentro de uma skill;
23
+ - preservar o Opus como protocolo e SDK, sem fazê-lo administrar o ciclo de vida do banco.
24
+
25
+ ## Alternativas consideradas
26
+
27
+ ### Manter scripts e comandos por projeto
28
+
29
+ É a opção mais barata no SDK, mas conserva descoberta, segurança, nomenclatura e verificação como
30
+ responsabilidades repetidas. O crescimento de cada projeto volta a produzir comandos paralelos e
31
+ contratos implícitos. Rejeitada.
32
+
33
+ ### Executar seeds somente por skills
34
+
35
+ Entrega uma interface conversacional conveniente, porém torna o comportamento dependente da
36
+ interpretação do agente e dificulta CI, depuração e automação sem agente. Uma skill também não é o
37
+ lugar adequado para persistir SQL ou regras de geração. Rejeitada como mecanismo de execução;
38
+ skills permanecem como clientes da CLI.
39
+
40
+ ### Tratar seed como action do runtime
41
+
42
+ Reutilizaria parte do pipeline de actions, mas misturaria dados de desenvolvimento com endpoints,
43
+ autorização de pessoas, manifest de produto e ciclo de vida do servidor. Seeds são operações de
44
+ projeto executadas pela CLI, não capacidades expostas pela aplicação. Rejeitada.
45
+
46
+ ### Declarar e vincular seeds em uma superfície própria
47
+
48
+ Uma declaração pura descreve identidade, versão, perfis, dependências e escopos permitidos. Um
49
+ binding server-only implementa planejamento, aplicação e verificação. O `opus.config.ts` registra
50
+ os bindings, e a CLI os carrega em subprocesso TypeScript. Skills operam essa CLI. Aceita.
51
+
52
+ ## Decisão
53
+
54
+ O pacote ganha a superfície `@softize/opus/seed` com dois construtores:
55
+
56
+ - `defineSeed` valida e preserva a declaração compartilhável;
57
+ - `bindSeed` anexa `plan`, `apply` e `verify`, produzindo o registrável server-only.
58
+
59
+ Uma declaração possui nome estável em lowercase com segmentos separados por ponto, versão inteira
60
+ positiva, descrição, um ou mais perfis, dependências opcionais e `safety.scopes`. Cada perfil pode
61
+ declarar descrição e métricas numéricas esperadas. O binding recebe o perfil escolhido, o escopo
62
+ explícito e a conexão produzida por `config.database()`, quando configurada. Planejamento e
63
+ verificação são obrigatórios; o Opus não considera uma função de escrita isolada um seed completo.
64
+
65
+ O `opus.config.ts` registra `seeds: SeedRegistryItem[]` no nível do projeto. Seeds não entram em
66
+ `DomainConfig`, no runtime de actions, em OpenAPI nem em endpoints. A CLI oferece:
67
+
68
+ - `opus seed list` para descoberta sem abrir o banco;
69
+ - `opus seed check` para validar registro, bindings, dependências, ciclos e comandos locais
70
+ paralelos;
71
+ - `opus seed plan <nome>` para descrever a aplicação sem escrever;
72
+ - `opus seed apply <nome>` para aplicar o perfil convergente;
73
+ - `opus seed verify <nome>` para conferir o estado atual sem escrever.
74
+
75
+ `plan`, `apply` e `verify` exigem `--scope` ou `OPUS_SEED_SCOPE`. O escopo precisa constar na
76
+ declaração e só pode ser `local`, `test` ou `isolated-preview`. Somente `NODE_ENV=development` e
77
+ `NODE_ENV=test` permitem operações que possam abrir o banco; valor ausente, desconhecido ou
78
+ `production` falha fechado, inclusive em `verify`. `list` e `check` continuam disponíveis porque
79
+ não abrem conexão.
80
+
81
+ Depois que `database()` abre a conexão, `assertSeedTarget({ database, scope })` precisa atestar o
82
+ destino real antes de qualquer binding. A verificação é obrigatória e server-only porque somente o
83
+ projeto conhece a identidade confiável do próprio banco. Repetir o rótulo informado na CLI ou
84
+ inferir apenas pela URL não constitui atestação. O retorno positivo `true` é parte do protocolo;
85
+ qualquer outro resultado falha fechado.
86
+
87
+ `apply` é convergente por contrato: repeti-lo com o mesmo perfil deve preservar o mesmo estado
88
+ observável. Reset, truncamento e remoção não fazem parte deste MVP. Um projeto que precise destruir
89
+ dados deverá manter uma operação separada, com autorização explícita, até que recorrência e
90
+ garantias suficientes justifiquem uma primitiva própria.
91
+
92
+ Dependências são executáveis, não apenas documentais. `plan`, `apply` e `verify` percorrem primeiro
93
+ a clausura de dependências em ordem topológica, com o perfil padrão de cada dependência e o mesmo
94
+ escopo explícito do alvo.
95
+
96
+ O gate detecta nomes e versões inválidos, perfis vazios, binding incompleto, seed duplicado,
97
+ dependência ausente, ciclo e scripts `seed*` ou `db:seed*` que não deleguem para `opus seed`. Testes
98
+ de integração do consumidor continuam responsáveis por provar idempotência contra seu banco; o
99
+ SDK não provisiona infraestrutura nem tenta inferir equivalência de estado a partir de um driver
100
+ genérico.
101
+
102
+ O registry distribui duas skills:
103
+
104
+ - `create-opus-seed`, para criar declaração, binding, registro e testes;
105
+ - `apply-opus-seed`, para descobrir, planejar, aplicar e verificar pela CLI.
106
+
107
+ As skills não contêm regra de negócio, credencial nem SQL. O resultado estruturado da CLI é a fonte
108
+ que elas usam para relatar métricas.
109
+
110
+ ## Consequências
111
+
112
+ Consumidores ganham uma porta única e testável sem entregar ao Opus o domínio dos dados. A
113
+ separação adiciona um novo registrável ao `opus.config.ts`, um subprocesso de CLI e um gate
114
+ específico. Projetos precisam migrar comandos existentes e tornar seu escopo de dados explícito.
115
+
116
+ Como o MVP não possui ledger, `status` significa verificar cada seed, não consultar histórico. Ele
117
+ também não prova idempotência universal: o binding e o teste persistente do consumidor continuam
118
+ responsáveis por essa garantia. Ledger, reset genérico, paralelismo entre seeds e uma interface
119
+ gráfica ficam fora desta decisão até existir uso real suficiente.
120
+
121
+ ## Verificação
122
+
123
+ - testes unitários de `defineSeed`, `bindSeed` e validação do grafo;
124
+ - testes do runner para listagem sem banco, proteção de escopo e bloqueio de produção;
125
+ - testes da CLI para `list`, `check`, `plan`, `apply` e `verify`;
126
+ - teste do gate contra scripts paralelos e dependências circulares;
127
+ - validação das skills com casos positivo, negativo e de execução;
128
+ - typecheck, suíte do pacote, `opus check` e gates de materialização verdes;
129
+ - adoção posterior em um consumidor real com aplicação repetida e teste persistente de
130
+ idempotência.
package/docs/protocol.md CHANGED
@@ -165,7 +165,7 @@ type FieldSpec = {
165
165
  order?: number
166
166
 
167
167
  // UI override
168
- widget?: string // 'text' | 'select' | 'date' | 'rich' | custom
168
+ widget?: FieldWidget // 'textarea' | 'code' | 'lines' | 'refItems' | 'icon' | 'toggle-group'
169
169
 
170
170
  // Options (select/radio/lookup)
171
171
  options?: OptionsSpec
@@ -174,6 +174,15 @@ type FieldSpec = {
174
174
  aiDescription?: string
175
175
  }
176
176
 
177
+ type FieldWidget =
178
+ | 'textarea'
179
+ | 'code'
180
+ | 'lines'
181
+ | 'refItems'
182
+ | 'icon'
183
+ | 'toggle-group'
184
+ | string // identificador próprio de outro renderer
185
+
177
186
  type OptionsSpec =
178
187
  | { kind: 'static', items: { value: string, label: I18nRef }[] }
179
188
  | { kind: 'dictionary', ref: string } // catálogo nomeado
package/docs/seeds.md ADDED
@@ -0,0 +1,132 @@
1
+ # Seeds estruturados
2
+
3
+ Seed é um dataset intencional e verificável para desenvolvimento, teste ou demonstração. No
4
+ Opus, ele não é uma action da aplicação nem um script livre: a declaração descreve o que existe e
5
+ o binding server-only sabe planejar, aplicar e verificar esse estado.
6
+
7
+ Essa separação mantém credenciais, banco e regras de geração no projeto consumidor. A CLI oferece
8
+ uma porta única para pessoas, CI e skills sem transformar o SDK em gerenciador do banco.
9
+
10
+ ## Contrato
11
+
12
+ ```ts
13
+ // src/seeds/customers-scenarios.definition.ts
14
+ import { defineSeed } from '@softize/opus/seed'
15
+
16
+ export const customersScenariosDefinition = defineSeed({
17
+ name: 'customers.scenarios',
18
+ version: 1,
19
+ description: 'Clientes representativos, incluindo conflitos de identidade.',
20
+ profiles: {
21
+ smoke: {
22
+ description: 'Conjunto mínimo para desenvolvimento diário.',
23
+ expected: { people: 8, openConflicts: 3 },
24
+ },
25
+ volume: {
26
+ description: 'Volume suficiente para paginação e busca.',
27
+ expected: { people: 500, openConflicts: 25 },
28
+ },
29
+ },
30
+ defaultProfile: 'smoke',
31
+ dependsOn: ['foundation.users'],
32
+ safety: { scopes: ['local', 'isolated-preview'] },
33
+ })
34
+ ```
35
+
36
+ O nome e a versão identificam o dataset. Perfis variam quantidade ou composição sem criar
37
+ comandos paralelos. `expected` declara métricas que `verify` precisa encontrar. Dependências
38
+ precisam estar registradas e não podem formar ciclos. Ao operar um seed, a CLI percorre primeiro
39
+ suas dependências na ordem topológica, usando o perfil padrão de cada uma e o mesmo escopo.
40
+
41
+ ## Binding e registro
42
+
43
+ ```ts
44
+ // src/server/seeds/customers-scenarios.seed.ts
45
+ import type { Kysely } from 'kysely'
46
+ import { bindSeed } from '@softize/opus/seed'
47
+ import { customersScenariosDefinition } from '../../seeds/customers-scenarios.definition.ts'
48
+ import type { Database } from '../database.ts'
49
+
50
+ export const customersScenariosSeed = bindSeed<Kysely<Database>>(customersScenariosDefinition, {
51
+ plan: ({ profile }) => ({
52
+ summary: `Preparar o perfil ${profile}.`,
53
+ operations: ['Convergir clientes conhecidos', 'Recalcular a projeção de conflitos'],
54
+ }),
55
+ apply: async ({ database, profile }) => {
56
+ const metrics = await convergeCustomerScenarios(database, profile)
57
+ return { summary: 'Cenários aplicados.', metrics }
58
+ },
59
+ verify: async ({ database, profile }) => {
60
+ const metrics = await inspectCustomerScenarios(database, profile)
61
+ return { summary: 'Cenários conferidos.', metrics }
62
+ },
63
+ })
64
+ ```
65
+
66
+ ```ts
67
+ // opus.config.ts
68
+ import type { Kysely } from 'kysely'
69
+ import type { SeedTargetAssertion } from '@softize/opus/seed'
70
+ import type { Database } from './src/server/database.ts'
71
+
72
+ const assertSeedTarget: SeedTargetAssertion<Kysely<Database>> = async ({ database, scope }) => {
73
+ if (database === null) {
74
+ throw new Error('A conexão de seed não foi aberta')
75
+ }
76
+ const target = await inspectSeedTarget(database)
77
+ if (target.scope !== scope || target.production) {
78
+ throw new Error('A conexão não corresponde ao escopo de seed solicitado')
79
+ }
80
+ return true
81
+ }
82
+
83
+ export default {
84
+ domains,
85
+ database: createDatabase,
86
+ assertSeedTarget,
87
+ seeds: [foundationUsersSeed, customersScenariosSeed],
88
+ }
89
+ ```
90
+
91
+ `apply` deve convergir: duas execuções com o mesmo perfil produzem o mesmo estado observável.
92
+ Use chaves estáveis e upsert/merge explícito no consumidor. Ausência de um registro no perfil não
93
+ autoriza removê-lo; reset, truncate e limpeza genérica não fazem parte deste contrato.
94
+
95
+ `database()` é lazy. `seed list` e `seed check` não a chamam. Os demais comandos abrem a conexão,
96
+ executam `assertSeedTarget` antes de qualquer binding e sempre tentam encerrá-la com `destroy()`.
97
+ A atestação pertence ao consumidor porque só ele conhece o destino real: consulte metadados do
98
+ banco ou outra identidade confiável da conexão; não compare apenas a URL ou repita o rótulo vindo
99
+ da CLI. A função precisa retornar `true` explicitamente depois da conferência; `false`, `undefined`
100
+ ou ausência de retorno fazem `plan`, `apply` e `verify` falharem fechado. `seed check` não abre a
101
+ conexão nem executa a atestação: ele reprova a ausência da função no contrato de configuração.
102
+
103
+ ## Operação
104
+
105
+ ```bash
106
+ pnpm exec opus seed list
107
+ pnpm exec opus seed check
108
+ pnpm exec opus seed plan customers.scenarios --profile smoke --scope local
109
+ pnpm exec opus seed apply customers.scenarios --profile smoke --scope local
110
+ pnpm exec opus seed verify customers.scenarios --profile smoke --scope local
111
+ ```
112
+
113
+ O escopo também pode vir de `OPUS_SEED_SCOPE`, útil em uma execução isolada de CI. Ele precisa
114
+ constar em `safety.scopes` e só pode ser `local`, `test` ou `isolated-preview`. O escopo não é um nome de
115
+ ambiente inferido pela CLI. `plan`, `apply` e `verify` só abrem conexão quando `NODE_ENV` é
116
+ explicitamente `development` ou `test`.
117
+
118
+ `--json` entrega o mesmo resultado em formato estruturado. Isso permite que uma skill explique o
119
+ plano e as métricas sem interpretar logs livres.
120
+
121
+ ## Gate
122
+
123
+ `opus seed check` falha diante de binding incompleto, duplicidade, dependência ausente, ciclo ou
124
+ script `seed*`/`db:seed*` que não delegue para `opus seed`. O gate valida estrutura e segurança;
125
+ o teste persistente do consumidor continua responsável por provar que a aplicação repetida é
126
+ idempotente no banco real.
127
+
128
+ O manifest inclui somente as declarações. Funções, conexão e implementação do binding nunca são
129
+ serializadas.
130
+
131
+ Veja a decisão e os limites em
132
+ [ADR 0002](adr/0002-structured-seeds-are-declared-and-bound.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softize/opus",
3
- "version": "12.6.3",
3
+ "version": "12.7.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",
@@ -149,6 +149,10 @@
149
149
  "types": "./src/schema/openapi.ts",
150
150
  "default": "./src/schema/openapi.ts"
151
151
  },
152
+ "./seed": {
153
+ "types": "./src/seed/index.ts",
154
+ "default": "./src/seed/index.ts"
155
+ },
152
156
  "./server": {
153
157
  "types": "./src/server/index.ts",
154
158
  "default": "./src/server/index.ts"
@@ -2,6 +2,8 @@
2
2
 
3
3
  <!-- softize-skill-route: $implement-opus-change -->
4
4
  <!-- softize-skill-route: $upgrade-opus -->
5
+ <!-- softize-skill-route: $create-opus-seed -->
6
+ <!-- softize-skill-route: $apply-opus-seed -->
5
7
 
6
8
  Este repo usa `@softize/opus`, um SDK e protocolo de actions. A versão aplicada aos
7
9
  artefatos de agentes fica em `base.json`; cada app Opus mantém seu marcador `opus.json`.
@@ -13,6 +15,8 @@ artefatos de agentes fica em `base.json`; cada app Opus mantém seu marcador `op
13
15
  - Manter contrato compartilhável separado de banco, segredo e driver server-only; usar
14
16
  `defineContract` com `bindAction` quando cliente e servidor consomem a mesma action.
15
17
  - Não duplicar schemas, tipos de transporte, validação ou fetch que o contrato já fornece.
18
+ - Declarar datasets persistentes com `defineSeed` + `bindSeed`, registrá-los em `opus.config.ts`
19
+ e operá-los por `opus seed`; não criar comandos de seed paralelos nem reset implícito.
16
20
  - Regenerar o inventário com `opus copy` quando mudar copy em contrato ou componente Opus
17
21
  mapeado; o Opus classifica os papéis semânticos e a política universal de `@softize/base`
18
22
  valida o conteúdo.
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: apply-opus-seed
3
+ description: Opera seeds Opus estruturados com descoberta, gate, plano, aplicação e verificação em escopo isolado. Use quando a pessoa pedir para popular ou atualizar dados de desenvolvimento, demonstração ou teste já registrados no opus.config.ts.
4
+ ---
5
+
6
+ # Aplicar seed Opus
7
+
8
+ ## Resultado
9
+
10
+ Aplicar o perfil solicitado no escopo autorizado e relatar o plano e as métricas verificadas pela
11
+ CLI, sem executar scripts paralelos nem acessar produção.
12
+
13
+ ## Entradas
14
+
15
+ - projeto e `opus.config.ts` corretos;
16
+ - nome do seed e perfil desejado;
17
+ - escopo isolado explícito entre os declarados pelo seed.
18
+
19
+ ## Procedimento
20
+
21
+ 1. Rodar `opus seed list --json` e localizar o nome, perfil, versão, dependências e escopos.
22
+ 2. Rodar `opus seed check --json`; parar se houver binding incompleto, ciclo, dependência ausente ou
23
+ comando local paralelo.
24
+ 3. Se seed, perfil ou escopo não estiverem explícitos e não puderem ser inferidos sem risco, pedir
25
+ a decisão. Nunca escolher produção nem ampliar `safety.scopes` para fazer a execução passar.
26
+ 4. Rodar `opus seed plan <nome> --profile <perfil> --scope <escopo> --json` e comunicar operações.
27
+ 5. Se a pessoa autorizou aplicar dados, rodar `opus seed apply` com os mesmos argumentos.
28
+ 6. Rodar `opus seed verify` e relatar as métricas estruturadas. Se falhar, preservar o erro e
29
+ diagnosticar o binding; não compensar com limpeza ou SQL improvisado.
30
+
31
+ ## Verificação
32
+
33
+ O término exige `apply.ok=true`, `verify.ok=true` e métricas compatíveis com o perfil. Em CI ou
34
+ preview, `OPUS_SEED_SCOPE` pode fornecer o mesmo escopo explícito.
35
+
36
+ ## Limites
37
+
38
+ - Não executar `plan`, `apply` ou `verify` fora de `NODE_ENV=development` ou `NODE_ENV=test`.
39
+ - Não chamar scripts de seed legados quando `opus seed check` os reprovar.
40
+ - Não usar reset, truncate ou remoção para forçar métricas esperadas.
41
+ - `list` e `check` são leitura; `apply` exige autorização para alterar o escopo informado.
42
+
43
+ ## Recursos
44
+
45
+ - Use [avaliações](references/evaluations.md) ao evoluir esta skill.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Aplicar seed Opus"
3
+ short_description: "Planeja, aplica e verifica um dataset estruturado"
4
+ default_prompt: "Use $apply-opus-seed para aplicar este dataset no escopo isolado."
@@ -0,0 +1,8 @@
1
+ # Avaliações
2
+
3
+ - Positiva: “Suba o seed `customers.scenarios` no preview isolado e confira o resultado.” Deve
4
+ disparar, executar list/check/plan antes de apply e terminar com verify.
5
+ - Negativa: “Rode as migrations de produção.” Não deve disparar; seed não administra schema nem
6
+ produção.
7
+ - Execução: com dois perfis e dois escopos declarados, aplicar exatamente o perfil/escopo pedidos,
8
+ relatar as métricas JSON e recusar um escopo ausente ou não permitido.
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: create-opus-seed
3
+ description: Cria ou migra um seed Opus estruturado com declaração, binding, registro, perfis, métricas e teste persistente de idempotência. Use ao adicionar datasets de desenvolvimento, demonstração ou teste em projeto baseado no Opus.
4
+ ---
5
+
6
+ # Criar seed Opus
7
+
8
+ ## Resultado
9
+
10
+ Entregar um seed descoberto por `opus seed list`, aprovado por `opus seed check` e cuja aplicação
11
+ repetida converge para o mesmo estado observável no banco do consumidor.
12
+
13
+ ## Entradas
14
+
15
+ - situação que o dataset precisa representar e pessoas que o usarão;
16
+ - nome estável `<dominio>.<dataset>`, perfis e métricas verificáveis;
17
+ - escopos isolados permitidos e dependências de outros seeds;
18
+ - acesso real do projeto a repositório, serviço e banco.
19
+
20
+ ## Procedimento
21
+
22
+ 1. Inventariar scripts, fixtures e comandos existentes; preservar cenários válidos e identificar
23
+ reset, truncate ou limpeza implícita antes da migração.
24
+ 2. Rodar `node scripts/scaffold.mjs <nome>` a partir desta skill e usar a saída para criar a
25
+ declaração compartilhável e o binding server-only nos caminhos reais do projeto.
26
+ 3. Definir perfis por intenção, não por pessoa ou comando. Declarar em `expected` somente métricas
27
+ que `verify` consegue medir no estado persistido.
28
+ 4. Implementar `plan` sem escrita, `apply` convergente e `verify` sem escrita. Usar chaves estáveis;
29
+ não transformar ausência no perfil em autorização para excluir dados.
30
+ 5. Registrar o binding em `seeds` no `opus.config.ts`. Manter conexão, segredo, SQL e regras de
31
+ geração fora da declaração e da skill. Implementar `assertSeedTarget` com uma consulta à
32
+ identidade real do destino antes que qualquer binding seja executado e retornar `true` somente
33
+ depois da atestação bem-sucedida.
34
+ 6. Remover scripts `seed*`/`db:seed*` paralelos ou fazê-los delegar explicitamente para `opus seed`.
35
+ 7. Seguir o fluxo de testes do projeto e provar no banco de teste: primeira aplicação, segunda
36
+ aplicação com o mesmo resultado, verificação das métricas e falha segura em escopo proibido.
37
+
38
+ ## Verificação
39
+
40
+ Rodar `opus seed check`, `opus seed list`, `plan`, duas aplicações e `verify` no escopo isolado.
41
+ Depois executar typecheck, testes afetados e geração/check do manifest do projeto.
42
+
43
+ ## Limites
44
+
45
+ - Não executar contra produção; `safety.scopes` aceita somente `local`, `test` e `isolated-preview`, e a
46
+ CLI exige `NODE_ENV=development` ou `NODE_ENV=test` antes de abrir conexão.
47
+ - Não implementar reset, truncate ou remoção genérica como efeito de `apply`.
48
+ - Não guardar SQL, credencial, dados pessoais reais ou regras de negócio dentro desta skill.
49
+ - Não declarar idempotência apenas porque o comando terminou; comparar o estado após duas runs.
50
+
51
+ ## Recursos
52
+
53
+ - Leia [contrato e exemplo](references/contract.md) ao definir os arquivos.
54
+ - Use [avaliações](references/evaluations.md) ao evoluir esta skill.
55
+ - Execute [scaffold.mjs](scripts/scaffold.mjs) para criar a estrutura inicial.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Criar seed Opus"
3
+ short_description: "Estrutura um dataset seguro, verificável e convergente"
4
+ default_prompt: "Use $create-opus-seed para criar ou migrar este seed Opus."
@@ -0,0 +1,16 @@
1
+ # Contrato de autoria
2
+
3
+ A declaração usa `defineSeed` e pode ser importada por ferramentas sem abrir banco. Ela contém
4
+ nome, versão, descrição, perfis, dependências e escopos. O binding usa `bindSeed` no lado servidor
5
+ e implementa três funções obrigatórias:
6
+
7
+ - `plan`: descreve operações e não escreve;
8
+ - `apply`: converge o estado e retorna métricas;
9
+ - `verify`: lê o estado e retorna métricas comparáveis a `profiles[profile].expected`.
10
+
11
+ O `opus.config.ts` registra bindings em `seeds`, mantém `database()` lazy e implementa
12
+ `assertSeedTarget({ database, scope })` consultando a identidade real do alvo antes de qualquer
13
+ binding e retornando `true` somente após a conferência. Qualquer outro retorno falha fechado.
14
+ Repetir o rótulo da CLI ou confiar apenas na URL não atesta o banco. Nomeie
15
+ perfis pela intenção (`smoke`, `conflicts`, `volume`) e use identificadores determinísticos nos
16
+ registros. Se a regra exigir apagar o banco, isso é outra operação e não um seed Opus.
@@ -0,0 +1,8 @@
1
+ # Avaliações
2
+
3
+ - Positiva: “Crie um seed com cenários de conflito de clientes e perfis smoke e volume.” Deve
4
+ disparar, separar declaração/binding, registrar no config e exigir teste de duas aplicações.
5
+ - Negativa: “Crie fixtures unitárias em memória para este parser.” Não deve disparar quando não há
6
+ dataset persistente nem operação `opus seed`.
7
+ - Execução: migrar um `db:seed` existente, obter zero violações em `opus seed check`, aplicar duas
8
+ vezes em escopo isolado e observar as mesmas métricas no `verify`.
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+
3
+ export function seedFiles(name) {
4
+ validateName(name)
5
+ const stem = name.replaceAll('.', '-')
6
+ const definitionName = `${camel(name)}Definition`
7
+ const bindingName = `${camel(name)}Seed`
8
+ return {
9
+ definition: `import { defineSeed } from '@softize/opus/seed'
10
+
11
+ export const ${definitionName} = defineSeed({
12
+ name: '${name}',
13
+ version: 1,
14
+ description: 'Descreva a situação representada por este dataset.',
15
+ profiles: {
16
+ smoke: {
17
+ description: 'Menor conjunto útil para desenvolvimento.',
18
+ expected: { records: 0 },
19
+ },
20
+ },
21
+ defaultProfile: 'smoke',
22
+ safety: { scopes: ['local'] },
23
+ })
24
+ `,
25
+ binding: `import { bindSeed } from '@softize/opus/seed'
26
+
27
+ import { ${definitionName} } from './${stem}.definition.ts'
28
+
29
+ export const ${bindingName} = bindSeed(${definitionName}, {
30
+ plan: ({ profile }) => ({
31
+ summary: \`Preparar o perfil \${profile}.\`,
32
+ operations: ['Descreva as operações convergentes'],
33
+ }),
34
+ apply: async ({ database, profile }) => {
35
+ void database
36
+ void profile
37
+ throw new Error('Implemente a aplicação convergente antes de registrar este seed')
38
+ },
39
+ verify: async ({ database, profile }) => {
40
+ void database
41
+ void profile
42
+ throw new Error('Implemente a verificação das métricas antes de registrar este seed')
43
+ },
44
+ })
45
+ `,
46
+ }
47
+ }
48
+
49
+ export function seedFileNames(name) {
50
+ validateName(name)
51
+ const stem = name.replaceAll('.', '-')
52
+ return { definition: `${stem}.definition.ts`, binding: `${stem}.seed.ts` }
53
+ }
54
+
55
+ const isMain = process.argv[1] !== undefined && import.meta.url === new URL(`file://${process.argv[1]}`).href
56
+ if (isMain) {
57
+ const [name] = process.argv.slice(2)
58
+ try {
59
+ const generated = seedFiles(name)
60
+ const names = seedFileNames(name)
61
+ process.stdout.write(
62
+ `// ${names.definition}\n${generated.definition}\n// ${names.binding}\n${generated.binding}`,
63
+ )
64
+ } catch (error) {
65
+ console.error(error instanceof Error ? error.message : String(error))
66
+ process.exit(1)
67
+ }
68
+ }
69
+
70
+ function camel(value) {
71
+ return value.replace(/\.([a-z0-9])/g, (_, character) => character.toUpperCase())
72
+ }
73
+
74
+ function validateName(name) {
75
+ if (!/^[a-z][a-z0-9]*(?:\.[a-z][a-z0-9]*)+$/.test(name ?? '')) {
76
+ throw new Error('name deve usar segmentos lowercase, como customers.scenarios')
77
+ }
78
+ }
@@ -5,6 +5,7 @@ description: Implementa uma mudança em projeto baseado no Opus preservando cont
5
5
  <!-- softize-skill-route: $create-opus-action -->
6
6
  <!-- softize-skill-route: $test-opus-action -->
7
7
  <!-- softize-skill-route: $build-opus-ui -->
8
+ <!-- softize-skill-route: $create-opus-seed -->
8
9
 
9
10
  # Implementar mudança Opus
10
11
 
@@ -25,7 +26,8 @@ dependências server-only, bindings registrados, testes e gates verdes.
25
26
  2. Modelar primeiro o contrato observável: nome, descrição, input, output, erros e metadata.
26
27
  3. Manter código compartilhável fora de banco, segredo, filesystem e drivers server-only.
27
28
  4. Implementar o binding e registrar o `ActionDef` no domínio/runtime conforme a topologia local.
28
- 5. Carregar e seguir `$create-opus-action`, `$test-opus-action` ou `$build-opus-ui` antes
29
+ 5. Carregar e seguir `$create-opus-action`, `$test-opus-action`, `$build-opus-ui` ou
30
+ `$create-opus-seed` antes
29
31
  do passo correspondente quando a mudança entrar nesses workflows especializados.
30
32
  6. Atualizar manifest, docs geradas e exemplos somente pelos comandos do repo.
31
33
 
package/src/core/index.ts CHANGED
@@ -54,6 +54,8 @@ export type {
54
54
  Example,
55
55
  ActionMessages,
56
56
  OptionsSpec,
57
+ BuiltInFieldWidget,
58
+ FieldWidget,
57
59
  FieldSpec,
58
60
  FilterOp,
59
61
  FilterSpec,
package/src/core/types.ts CHANGED
@@ -529,6 +529,21 @@ export type OptionsSpec =
529
529
  | { kind: 'dictionary'; ref: string }
530
530
  | { kind: 'lookup'; source: string; depends?: string[] }
531
531
 
532
+ /** Overrides declarativos nativos reconhecidos pelo ActionForm. */
533
+ export type BuiltInFieldWidget =
534
+ | 'textarea'
535
+ | 'code'
536
+ | 'lines'
537
+ | 'refItems'
538
+ | 'icon'
539
+ | 'toggle-group'
540
+
541
+ /**
542
+ * Widget nativo ou identificador próprio de outro renderer.
543
+ * A interseção preserva autocomplete dos nativos sem quebrar widgets customizados existentes.
544
+ */
545
+ export type FieldWidget = BuiltInFieldWidget | (string & { readonly __opusCustomWidget?: never })
546
+
532
547
  /**
533
548
  * Descrição de campo em `FormAction.fields`. Drive UI rendering + AI tool
534
549
  * description per-field.
@@ -551,7 +566,7 @@ export interface FieldSpec {
551
566
  group?: string
552
567
  order?: number
553
568
 
554
- widget?: string
569
+ widget?: FieldWidget
555
570
  options?: OptionsSpec
556
571
 
557
572
  aiDescription?: string