@softize/opus 12.6.3 → 12.7.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.
- package/CHANGELOG.md +23 -1
- package/README.md +20 -2
- package/bin/cli.mjs +10 -1
- package/bin/lib/gen-manifest.mjs +1 -0
- package/bin/lib/gen-runner.mjs +29 -1
- package/bin/lib/seed-runner.mjs +152 -0
- package/bin/lib/seed.mjs +229 -0
- package/docs/adr/0002-structured-seeds-are-declared-and-bound.md +130 -0
- package/docs/protocol.md +10 -1
- package/docs/seeds.md +132 -0
- package/package.json +5 -1
- package/registry/instructions/opus.md +4 -0
- package/registry/skills/apply-opus-seed/SKILL.md +45 -0
- package/registry/skills/apply-opus-seed/agents/openai.yaml +4 -0
- package/registry/skills/apply-opus-seed/references/evaluations.md +8 -0
- package/registry/skills/create-opus-seed/SKILL.md +55 -0
- package/registry/skills/create-opus-seed/agents/openai.yaml +4 -0
- package/registry/skills/create-opus-seed/references/contract.md +16 -0
- package/registry/skills/create-opus-seed/references/evaluations.md +8 -0
- package/registry/skills/create-opus-seed/scripts/scaffold.mjs +78 -0
- package/registry/skills/implement-opus-change/SKILL.md +3 -1
- package/src/core/index.ts +2 -0
- package/src/core/types.ts +16 -1
- package/src/seed/index.ts +391 -0
- package/src/ui/components/patterns/form.tsx +131 -19
- package/src/ui/components/primitives/detail.tsx +113 -0
- package/src/ui/components/primitives/tabs.tsx +1 -1
- package/src/ui/docs/content/action-form.md +10 -0
- package/src/ui/docs/content/cli.md +17 -0
- package/src/ui/docs/content/detail.md +38 -0
- package/src/ui/docs/registry.tsx +2 -0
- package/src/ui/drivers/react.tsx +3 -2
- package/src/ui/lib/object-schema.ts +36 -0
- package/src/ui/meta.ts +7 -1
- 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?:
|
|
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.
|
|
3
|
+
"version": "12.7.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",
|
|
@@ -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,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,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
|
|
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
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?:
|
|
569
|
+
widget?: FieldWidget
|
|
555
570
|
options?: OptionsSpec
|
|
556
571
|
|
|
557
572
|
aiDescription?: string
|