@softize/opus 11.1.1 → 12.0.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 +63 -1
- package/README.md +31 -1
- package/bin/cli.mjs +97 -27
- package/bin/lib/check.mjs +55 -43
- package/bin/lib/copy.mjs +2202 -0
- package/bin/lib/create.mjs +227 -39
- package/bin/lib/db-migrate-runner.mjs +9 -6
- package/bin/lib/db-project-path.mjs +20 -0
- package/bin/lib/db-scaffold-runner.mjs +23 -8
- package/bin/lib/db.mjs +6 -4
- package/bin/lib/gen.mjs +60 -29
- package/bin/lib/init.mjs +212 -56
- package/bin/lib/introspect.mjs +3 -2
- package/bin/lib/materialize.mjs +623 -97
- package/bin/lib/postinstall.mjs +6 -5
- package/bin/lib/validate-skill.mjs +502 -30
- package/docs/code-style.md +142 -7
- package/docs/consumer-upgrade-propagation.md +4 -3
- package/docs/releasing.md +28 -17
- package/package.json +6 -1
- package/registry/git/pre-push.d/00-opus-copy +14 -0
- package/registry/git/pre-push.d/opus +7 -21
- package/registry/git/run-opus-pre-push.mjs +141 -0
- package/registry/hooks/opus-check-on-stop.mjs +13 -31
- package/registry/instructions/opus.md +11 -5
- package/registry/skills/build-opus-ui/SKILL.md +5 -4
- package/registry/skills/create-opus-action/SKILL.md +4 -4
- package/registry/skills/implement-opus-change/SKILL.md +7 -5
- package/registry/skills/upgrade-opus/SKILL.md +8 -4
- package/registry/skills/upgrade-opus/references/upgrade-checklist.md +4 -1
- package/registry/templates/app/package.json +4 -0
- package/registry/templates/app/pnpm-workspace.yaml +3 -2
- package/registry/templates/app/src/domains/tasks/actions/list.ts +1 -1
- package/registry/templates/monorepo/pnpm-workspace.yaml +3 -1
- package/src/ui/components/patterns/split.tsx +66 -24
- package/src/ui/docs/content/cli.md +8 -7
- package/src/ui/docs/content/communication.md +79 -126
- package/src/ui/docs/content/getting-started.md +29 -16
- package/src/ui/docs/content/split.md +43 -3
- package/src/ui/react.tsx +1 -1
- package/registry/skills/write-product-communication/SKILL.md +0 -28
- package/registry/skills/write-product-communication/agents/openai.yaml +0 -4
- package/registry/templates/app/_npmrc +0 -1
- package/registry/templates/monorepo/_npmrc +0 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: create-opus-action
|
|
3
3
|
description: Cria uma action Opus no split canônico de defineContract e bindAction, incluindo registro e validação. Use ao adicionar endpoint, comando, consulta ou operação de domínio em projeto baseado no Opus.
|
|
4
4
|
---
|
|
5
|
+
<!-- softize-skill-route: $test-opus-action -->
|
|
5
6
|
|
|
6
7
|
# Criar action Opus
|
|
7
8
|
|
|
@@ -21,16 +22,15 @@ registrados no runtime do projeto.
|
|
|
21
22
|
1. Rodar `node scripts/scaffold.mjs <resource> <verb> <kind>` a partir desta skill.
|
|
22
23
|
2. Colocar o contrato em módulo importável pelos consumidores e o binding no lado servidor.
|
|
23
24
|
3. Substituir os exemplos do scaffold por schemas, descrições e comportamento do domínio.
|
|
24
|
-
Usar `write-product-communication` nas descrições, mensagens e textos projetados para UI
|
|
25
|
-
ou documentação.
|
|
26
25
|
4. Declarar `requires` somente com `authorize` efetivo; `requires` sozinho não protege a action.
|
|
27
26
|
5. Exportar e registrar o binding no domínio/runtime existente.
|
|
28
|
-
6.
|
|
27
|
+
6. Carregar e seguir `$test-opus-action` para cobrir contrato, sucesso, erros e efeitos relevantes.
|
|
29
28
|
|
|
30
29
|
## Verificação
|
|
31
30
|
|
|
32
31
|
Rodar `opus check <escopo>`, typecheck e testes do domínio. Se o projeto gera manifest,
|
|
33
|
-
OpenAPI ou docs, regenerar e conferir o diff.
|
|
32
|
+
OpenAPI ou docs, regenerar e conferir o diff. Regenerar também o inventário com `opus copy`
|
|
33
|
+
e executar `base copy check` quando o contrato declarar texto humano.
|
|
34
34
|
|
|
35
35
|
## Limites
|
|
36
36
|
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
name: implement-opus-change
|
|
3
3
|
description: Implementa uma mudança em projeto baseado no Opus preservando contrato, binding, registro e projeções. Use ao alterar domínio, action, runtime ou integração que utilize @softize/opus.
|
|
4
4
|
---
|
|
5
|
+
<!-- softize-skill-route: $create-opus-action -->
|
|
6
|
+
<!-- softize-skill-route: $test-opus-action -->
|
|
7
|
+
<!-- softize-skill-route: $build-opus-ui -->
|
|
5
8
|
|
|
6
9
|
# Implementar mudança Opus
|
|
7
10
|
|
|
@@ -22,16 +25,15 @@ dependências server-only, bindings registrados, testes e gates verdes.
|
|
|
22
25
|
2. Modelar primeiro o contrato observável: nome, descrição, input, output, erros e metadata.
|
|
23
26
|
3. Manter código compartilhável fora de banco, segredo, filesystem e drivers server-only.
|
|
24
27
|
4. Implementar o binding e registrar o `ActionDef` no domínio/runtime conforme a topologia local.
|
|
25
|
-
5.
|
|
26
|
-
nesses workflows especializados.
|
|
28
|
+
5. Carregar e seguir `$create-opus-action`, `$test-opus-action` ou `$build-opus-ui` antes
|
|
29
|
+
do passo correspondente quando a mudança entrar nesses workflows especializados.
|
|
27
30
|
6. Atualizar manifest, docs geradas e exemplos somente pelos comandos do repo.
|
|
28
|
-
7. Usar `write-product-communication` sempre que a mudança alcançar documentação, UI, CLI,
|
|
29
|
-
mensagens de erro ou outro texto destinado a uma pessoa.
|
|
30
31
|
|
|
31
32
|
## Verificação
|
|
32
33
|
|
|
33
34
|
Rodar `opus check` no escopo correto, testes afetados, typecheck e os geradores que o
|
|
34
|
-
projeto declara.
|
|
35
|
+
projeto declara. Se o contrato mudou texto humano, regenerar com `opus copy` e executar
|
|
36
|
+
`opus copy --check` seguido de `base copy check`. Conferir o diff gerado antes do handoff.
|
|
35
37
|
|
|
36
38
|
## Limites
|
|
37
39
|
|
|
@@ -15,12 +15,16 @@ materializados e projeções geradas consistentes no mesmo diff.
|
|
|
15
15
|
1. Registrar versão atual e alvo e ler todas as entradas intermediárias do `CHANGELOG.md`,
|
|
16
16
|
priorizando seções Breaking e instruções de migração.
|
|
17
17
|
2. Atualizar a dependência com o package manager do repo.
|
|
18
|
-
3. Executar `
|
|
18
|
+
3. Executar o script do projeto (`pnpm run setup` ou, no monorepo,
|
|
19
|
+
`pnpm --filter <workspace> run setup`) para materializar Base e Opus na ordem declarada.
|
|
20
|
+
`pnpm setup` sem `run` continua reservado ao setup da própria máquina.
|
|
19
21
|
4. Aplicar migrações no código por domínio, sem compatibilidade temporária silenciosa.
|
|
20
22
|
5. Rodar `opus check`, typecheck, testes, build e geradores declarados pelo projeto.
|
|
21
|
-
6.
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
6. Remover configuração obsoleta da registry privada: o setup limpa a diretiva conhecida
|
|
24
|
+
no `.npmrc` do projeto; diagnosticar também `pnpm config get @softize:registry --global`
|
|
25
|
+
e, se apontar para `registry.softize.com.br`, executar
|
|
26
|
+
`pnpm config delete @softize:registry --global` com autorização do dono da máquina.
|
|
27
|
+
7. Conferir `base.json`, lockfile, skills, hooks, instruções e outputs gerados no diff.
|
|
24
28
|
|
|
25
29
|
## Verificação
|
|
26
30
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# Checklist de upgrade
|
|
2
2
|
|
|
3
3
|
- versão alvo presente em `package.json`, lockfile, `base.json` e `opus.json` aplicáveis;
|
|
4
|
-
- `
|
|
4
|
+
- `pnpm run setup` (ou `pnpm --filter <workspace> run setup`) e `opus check` verdes;
|
|
5
|
+
- `.npmrc` do projeto sem `@softize:registry=...registry.softize.com.br` e configuração
|
|
6
|
+
global antiga removida com autorização explícita;
|
|
7
|
+
- `opus copy --check` e `base copy check` verdes quando o inventário estiver configurado;
|
|
5
8
|
- breakings intermediários tratados;
|
|
6
9
|
- typecheck, testes e build verdes;
|
|
7
10
|
- manifest, OpenAPI e docs regenerados quando configurados;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
|
+
"setup": "opus setup && base setup",
|
|
6
7
|
"dev": "vite",
|
|
7
8
|
"dev:design": "vite --mode design --configLoader runner",
|
|
8
9
|
"build": "tsc --noEmit && vite build",
|
|
@@ -11,6 +12,8 @@
|
|
|
11
12
|
"test": "vitest run",
|
|
12
13
|
"format": "prettier --write src",
|
|
13
14
|
"format:check": "prettier --check src",
|
|
15
|
+
"copy": "opus copy",
|
|
16
|
+
"copy:check": "opus copy --check && base copy check",
|
|
14
17
|
"manifest": "opus gen",
|
|
15
18
|
"manifest:check": "opus gen && git diff --exit-code .opus/manifest.json"
|
|
16
19
|
},
|
|
@@ -27,6 +30,7 @@
|
|
|
27
30
|
"zod": "^3.24.0"
|
|
28
31
|
},
|
|
29
32
|
"devDependencies": {
|
|
33
|
+
"@softize/base": "^2.0.0",
|
|
30
34
|
"@tailwindcss/vite": "^4.1.0",
|
|
31
35
|
"@types/node": "^22.0.0",
|
|
32
36
|
"@types/react": "^19.0.0",
|
|
@@ -5,7 +5,8 @@ allowBuilds:
|
|
|
5
5
|
'@softize/opus': true
|
|
6
6
|
esbuild: true
|
|
7
7
|
|
|
8
|
-
#
|
|
9
|
-
#
|
|
8
|
+
# Opus e Base são publicados juntos no npm público: os dois ficam fora da quarentena
|
|
9
|
+
# para que um rollout recém-publicado não combine protocolo novo com política antiga.
|
|
10
10
|
minimumReleaseAgeExclude:
|
|
11
11
|
- '@softize/opus'
|
|
12
|
+
- '@softize/base'
|
|
@@ -24,7 +24,7 @@ export const taskList = defineContract({
|
|
|
24
24
|
|
|
25
25
|
// Fixture em memória — o domínio-exemplo não tem banco; troque pelo seu repositório.
|
|
26
26
|
const TASKS = [
|
|
27
|
-
{ id: '1', title: 'Conhecer
|
|
27
|
+
{ id: '1', title: 'Conhecer o Opus.', done: true },
|
|
28
28
|
{ id: '2', title: 'Modelar o primeiro domínio real.', done: false },
|
|
29
29
|
]
|
|
30
30
|
|
|
@@ -9,6 +9,8 @@ allowBuilds:
|
|
|
9
9
|
'@softize/opus': true
|
|
10
10
|
esbuild: true
|
|
11
11
|
|
|
12
|
-
#
|
|
12
|
+
# Opus e Base são publicados juntos no npm público; não deixe a quarentena montar uma
|
|
13
|
+
# combinação de versões que nunca passou pelos gates da release.
|
|
13
14
|
minimumReleaseAgeExclude:
|
|
14
15
|
- '@softize/opus'
|
|
16
|
+
- '@softize/base'
|
|
@@ -2,13 +2,21 @@ import { Children, isValidElement, type CSSProperties, type ReactElement, type R
|
|
|
2
2
|
import { cn } from '../../lib/cn.ts'
|
|
3
3
|
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '../primitives/resizable.tsx'
|
|
4
4
|
|
|
5
|
+
export type PaneSize = number | `${number}%` | `${number}rem` | `${number}em` | `${number}vh` | `${number}vw` | `${number}px`
|
|
6
|
+
export type SplitLayout = Record<string, number>
|
|
7
|
+
export interface SplitLayoutChange {
|
|
8
|
+
isUserInteraction: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
export interface PaneProps {
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
+
/** Identidade estável usada pelo layout redimensionável e por layouts persistidos. */
|
|
13
|
+
id?: string
|
|
14
|
+
/** Tamanho inicial. Números representam porcentagens; strings aceitam %, rem, em, vh, vw e px. */
|
|
15
|
+
initialSize?: PaneSize
|
|
16
|
+
/** Tamanho mínimo quando o split é redimensionável. */
|
|
17
|
+
minSize?: PaneSize
|
|
18
|
+
/** Tamanho máximo quando o split é redimensionável. */
|
|
19
|
+
maxSize?: PaneSize
|
|
12
20
|
/** Ocupa o espaço remanescente no layout simples. */
|
|
13
21
|
grow?: boolean
|
|
14
22
|
/** Respiro interno padrão. Use `none` para chrome, navegação ou conteúdo com inset próprio. */
|
|
@@ -18,21 +26,27 @@ export interface PaneProps {
|
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
/** Um conteúdo encaixável. Sua posição é dada pela ordem dentro de <Split>. */
|
|
21
|
-
export function Pane({ initialSize, grow = false, inset = 'md', className, children }: PaneProps): React.ReactElement {
|
|
22
|
-
const style: CSSProperties | undefined = initialSize === undefined ? undefined : { flexBasis:
|
|
29
|
+
export function Pane({ id, initialSize, grow = false, inset = 'md', className, children }: PaneProps): React.ReactElement {
|
|
30
|
+
const style: CSSProperties | undefined = initialSize === undefined ? undefined : { flexBasis: sizeToCss(initialSize) }
|
|
23
31
|
const insets = { none: '', sm: 'p-2', md: 'p-4', lg: 'p-6' } as const
|
|
24
32
|
return (
|
|
25
|
-
<div data-slot="pane" data-inset={inset} className={cn('min-h-0 min-w-0 shrink-0', grow && 'flex-1', insets[inset], className)} style={style}>
|
|
33
|
+
<div id={id === undefined ? undefined : String(id)} data-slot="pane" data-inset={inset} className={cn('min-h-0 min-w-0 shrink-0', grow && 'flex-1', insets[inset], className)} style={style}>
|
|
26
34
|
{children}
|
|
27
35
|
</div>
|
|
28
36
|
)
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
export interface SplitProps {
|
|
40
|
+
/** Identidade estável do grupo redimensionável. */
|
|
41
|
+
id?: string
|
|
32
42
|
direction?: 'horizontal' | 'vertical'
|
|
33
|
-
/** Quando ligado, cada fronteira ganha um
|
|
43
|
+
/** Quando ligado, cada fronteira ganha um separador acessível e arrastável. */
|
|
34
44
|
resizable?: boolean
|
|
35
45
|
handle?: boolean
|
|
46
|
+
/** Layout percentual restaurado, indexado pelos ids dos panes. */
|
|
47
|
+
defaultLayout?: SplitLayout
|
|
48
|
+
/** Chamado ao concluir uma mudança de layout; pode persistir o resultado em localStorage. */
|
|
49
|
+
onLayoutChanged?: (layout: SplitLayout, meta: SplitLayoutChange) => void
|
|
36
50
|
className?: string
|
|
37
51
|
children: ReactNode
|
|
38
52
|
}
|
|
@@ -43,11 +57,20 @@ function panesOf(children: ReactNode): ReactElement<PaneProps>[] {
|
|
|
43
57
|
)
|
|
44
58
|
}
|
|
45
59
|
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
// Números continuam percentuais por compatibilidade com Split/Pane. Strings preservam
|
|
61
|
+
// a unidade explícita para layouts que precisam de um limite físico, como `28rem`.
|
|
62
|
+
function sizeToCss(value: PaneSize): string {
|
|
63
|
+
return typeof value === 'number' ? `${value}%` : value
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function resizableSize(value: PaneSize | undefined): string | undefined {
|
|
67
|
+
return value === undefined ? undefined : sizeToCss(value)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function percentage(value: PaneSize | undefined): number | undefined {
|
|
71
|
+
if (typeof value === 'number') return value
|
|
72
|
+
if (value?.endsWith('%')) return Number.parseFloat(value)
|
|
73
|
+
return undefined
|
|
51
74
|
}
|
|
52
75
|
|
|
53
76
|
/**
|
|
@@ -55,35 +78,54 @@ function percent(value: number | undefined): string | undefined {
|
|
|
55
78
|
* quando fixa e o primitive Resizable quando `resizable`; quem consome não troca de
|
|
56
79
|
* modelo de layout para ganhar arraste.
|
|
57
80
|
*/
|
|
58
|
-
export function Split({
|
|
81
|
+
export function Split({
|
|
82
|
+
id,
|
|
83
|
+
direction = 'horizontal',
|
|
84
|
+
resizable = false,
|
|
85
|
+
handle = false,
|
|
86
|
+
defaultLayout,
|
|
87
|
+
onLayoutChanged,
|
|
88
|
+
className,
|
|
89
|
+
children,
|
|
90
|
+
}: SplitProps): React.ReactElement {
|
|
59
91
|
const panes = panesOf(children)
|
|
60
92
|
const vertical = direction === 'vertical'
|
|
61
93
|
|
|
62
94
|
if (!resizable) {
|
|
63
95
|
return (
|
|
64
|
-
<div data-slot="split" data-direction={direction} className={cn('flex min-h-0 min-w-0 flex-1', vertical && 'flex-col', className)}>
|
|
96
|
+
<div id={id === undefined ? undefined : String(id)} data-slot="split" data-direction={direction} className={cn('flex min-h-0 min-w-0 flex-1', vertical && 'flex-col', className)}>
|
|
65
97
|
{panes}
|
|
66
98
|
</div>
|
|
67
99
|
)
|
|
68
100
|
}
|
|
69
101
|
|
|
70
|
-
const fixed = panes.reduce((sum, pane) => sum + (pane.props.initialSize ?? 0), 0)
|
|
102
|
+
const fixed = panes.reduce((sum, pane) => sum + (percentage(pane.props.initialSize) ?? 0), 0)
|
|
103
|
+
const hasAbsoluteSize = panes.some((pane) => typeof pane.props.initialSize === 'string' && !pane.props.initialSize.endsWith('%'))
|
|
71
104
|
const growing = panes.filter((pane) => pane.props.grow)
|
|
72
105
|
const rest = Math.max(0, 100 - fixed)
|
|
73
106
|
const fallback = panes.length === 0 ? 100 : 100 / panes.length
|
|
74
107
|
|
|
75
108
|
return (
|
|
76
|
-
<ResizablePanelGroup
|
|
109
|
+
<ResizablePanelGroup
|
|
110
|
+
id={id}
|
|
111
|
+
orientation={direction}
|
|
112
|
+
defaultLayout={defaultLayout}
|
|
113
|
+
onLayoutChanged={onLayoutChanged}
|
|
114
|
+
data-slot="split"
|
|
115
|
+
className={cn('min-h-0 min-w-0 flex-1', className)}
|
|
116
|
+
>
|
|
77
117
|
{panes.flatMap((pane, index) => {
|
|
78
|
-
const size = pane.props.initialSize
|
|
118
|
+
const size = pane.props.initialSize
|
|
119
|
+
?? (hasAbsoluteSize ? undefined : pane.props.grow ? rest / Math.max(1, growing.length) : fallback)
|
|
79
120
|
const panel = (
|
|
80
121
|
<ResizablePanel
|
|
81
122
|
key={pane.key ?? index}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
123
|
+
id={pane.props.id}
|
|
124
|
+
defaultSize={resizableSize(size)}
|
|
125
|
+
minSize={resizableSize(pane.props.minSize)}
|
|
126
|
+
maxSize={resizableSize(pane.props.maxSize)}
|
|
85
127
|
>
|
|
86
|
-
<Pane {...pane.props} className={cn('h-full w-full', pane.props.className)} />
|
|
128
|
+
<Pane {...pane.props} id={undefined} className={cn('h-full w-full', pane.props.className)} />
|
|
87
129
|
</ResizablePanel>
|
|
88
130
|
)
|
|
89
131
|
return index === 0 ? [panel] : [<ResizableHandle key={`handle-${pane.key ?? index}`} withHandle={handle} />, panel]
|
|
@@ -32,20 +32,21 @@ opus introspect --json
|
|
|
32
32
|
## Bootstrap e templates
|
|
33
33
|
|
|
34
34
|
> `create` scaffolda um app novo com os pré-requisitos plugados; `setup` é per-app
|
|
35
|
-
> (idempotente, nunca sobrescreve o seu); `add` copia um template do
|
|
35
|
+
> (idempotente, nunca sobrescreve o seu); `add` copia um template do catálogo empacotado.
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
opus create meu-app # app canônico do zero: protocolo + UI + preview +
|
|
38
|
+
opus create meu-app # app canônico do zero: protocolo + UI + preview + automação
|
|
39
39
|
opus create meu-cliente --monorepo # a RAIZ de um workspace (apps/* + packages/*)
|
|
40
40
|
opus create apps/portal # dentro de um workspace: só o app (modo detectado)
|
|
41
|
-
opus setup # grava opus.json
|
|
41
|
+
opus setup # grava opus.json e materializa a camada específica do SDK
|
|
42
42
|
opus list # lista os templates disponíveis
|
|
43
|
-
opus add action-form # copia um template do
|
|
43
|
+
opus add action-form # copia um template do catálogo pro projeto
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
O esqueleto do `create` versiona com o Opus (sai do mesmo pacote que o SDK que ele
|
|
47
|
-
configura) e nasce com os gates verdes: domínio-exemplo canônico, teste, manifest
|
|
48
|
-
|
|
47
|
+
configura) e nasce com os gates verdes: domínio-exemplo canônico, teste, manifest e o dev
|
|
48
|
+
server pronto pro preview do Maestro. O método geral, a memória e a revisão vêm da Base
|
|
49
|
+
depois de `pnpm run setup`. Dois modos, por detecção:
|
|
49
50
|
repo standalone (template inteiro) ou **app em monorepo** (dentro de um workspace pnpm:
|
|
50
51
|
só os arquivos do app; o que a raiz precisa ter vira aviso, sem clobber).
|
|
51
52
|
|
|
@@ -55,5 +56,5 @@ só os arquivos do app; o que a raiz precisa ter vira aviso, sem clobber).
|
|
|
55
56
|
> action no formato canônico sem decorar convenção.
|
|
56
57
|
|
|
57
58
|
`opus mcp` sobe o server (configurado em `.mcp.json`): o agente consulta a estrutura, roda o
|
|
58
|
-
check e gera o esqueleto de uma action nova pela skill
|
|
59
|
+
check e gera o esqueleto de uma action nova pela skill `$create-opus-action` — a mesma régua do
|
|
59
60
|
`opus check`, por construção.
|
|
@@ -2,129 +2,82 @@
|
|
|
2
2
|
title: Comunicação
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
# Comunicação
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| Situação | Prefira | Evite |
|
|
85
|
-
|---|---|---|
|
|
86
|
-
| Serviço indisponível | O Multica não respondeu. Tente novamente em alguns instantes. | ERRO: request failed (500) |
|
|
87
|
-
| Recurso ausente | Este workspace não existe mais. Volte à lista para escolher outro. | Workspace inválido. |
|
|
88
|
-
| Permissão | Você não tem acesso a este workspace. Peça acesso a um administrador. | Acesso negado. |
|
|
89
|
-
| Validação | Informe um endereço de e-mail válido. | Campo inválido. |
|
|
90
|
-
|
|
91
|
-
Quando não houver uma ação possível, diga isso com honestidade e preserve o trabalho já feito.
|
|
92
|
-
Não ofereça “tente novamente” como resposta automática para falhas que uma nova tentativa não
|
|
93
|
-
pode resolver.
|
|
94
|
-
|
|
95
|
-
## Textos de interface
|
|
96
|
-
|
|
97
|
-
Código e valores internos permanecem em inglês. O que uma pessoa lê na interface usa pt-BR,
|
|
98
|
-
salvo quando o produto declarar outro idioma.
|
|
99
|
-
|
|
100
|
-
- Frases começam com maiúscula e terminam com ponto.
|
|
101
|
-
- Labels, títulos, itens de menu e botões usam sentence case e não levam ponto.
|
|
102
|
-
- Placeholders de seleção usam `Selecione <artigo> <coisa>…`.
|
|
103
|
-
- Placeholders ajudam a responder, mas não substituem o label do campo.
|
|
104
|
-
- Botões descrevem a ação que executarão, como `Publicar` ou `Salvar alterações`.
|
|
105
|
-
- Textos de ajuda explicam o efeito percebido, não a implementação interna.
|
|
106
|
-
- Abreviações são usadas somente quando o espaço ou o vocabulário do domínio as justificam.
|
|
107
|
-
|
|
108
|
-
| Tipo | Prefira | Evite |
|
|
109
|
-
|---|---|---|
|
|
110
|
-
| Frase | A configuração não carregou. | Config indisponível. |
|
|
111
|
-
| Label | Nova sessão | Nova sessão. |
|
|
112
|
-
| Botão | Publicar | PUBLICAR |
|
|
113
|
-
| Placeholder | Selecione um cliente… | Selecionar |
|
|
114
|
-
| Ajuda | Os agentes deste workspace passam a usar esta skill. | O push sobrescreve o runtime. |
|
|
115
|
-
|
|
116
|
-
## Revisão
|
|
117
|
-
|
|
118
|
-
Antes de entregar, releia o texto fora do contexto da implementação e confirme:
|
|
119
|
-
|
|
120
|
-
- o leitor consegue reconhecer a situação sem conhecer o código;
|
|
121
|
-
- conceitos novos são explicados antes de orientar decisões;
|
|
122
|
-
- regras obrigatórias estão separadas de recomendações;
|
|
123
|
-
- a razão da orientação aparece quando ajuda a compreendê-la;
|
|
124
|
-
- mensagens de erro preservam a calma e oferecem uma saída real;
|
|
125
|
-
- a frase soa como uma conversa profissional, não como log, slogan ou ordem interna;
|
|
126
|
-
- detalhes técnicos permanecem disponíveis no lugar adequado sem dominar a comunicação.
|
|
127
|
-
|
|
128
|
-
Leia o fluxo completo, não apenas strings isoladas. Um conjunto de frases corretas ainda pode
|
|
129
|
-
produzir uma experiência confusa quando repete informações, muda de vocabulário ou apresenta
|
|
130
|
-
ações fora da ordem em que a pessoa precisa delas.
|
|
5
|
+
# Comunicação do Opus
|
|
6
|
+
|
|
7
|
+
Esta página registra somente o vocabulário, as superfícies e os mapeamentos próprios do
|
|
8
|
+
Opus. Princípios gerais de comunicação humana — contexto, impacto, próxima ação, gramática,
|
|
9
|
+
pontuação e consistência — pertencem à skill `write-product-communication` da
|
|
10
|
+
`@softize/base`. As regras mecanicamente verificáveis de copy ficam na política versionada,
|
|
11
|
+
em `node_modules/@softize/base/docs/copy-policy.md`, e no catálogo executável
|
|
12
|
+
`node_modules/@softize/base/policies/copy.json`.
|
|
13
|
+
|
|
14
|
+
A separação é intencional: uma mudança editorial universal acontece na Base; esta página
|
|
15
|
+
muda apenas quando o produto ou os componentes Opus mudam.
|
|
16
|
+
|
|
17
|
+
## Língua e termos no Opus
|
|
18
|
+
|
|
19
|
+
| Camada | Convenção local |
|
|
20
|
+
|---|---|
|
|
21
|
+
| Identificadores de código, arquivos, diretórios e domínios | Inglês |
|
|
22
|
+
| Nomes de action e rota | Inglês, no formato `resource.verb` |
|
|
23
|
+
| Colunas e tabelas de banco | Inglês, em `snake_case` |
|
|
24
|
+
| Enums, status e chaves de dicionário | Inglês, inclusive em exemplos e fixtures (`active`, não `ativo`) |
|
|
25
|
+
| Texto de UI visível | pt-BR; o contrato fornece o label humano para valores técnicos |
|
|
26
|
+
| Comentários e documentação deste repositório | pt-BR |
|
|
27
|
+
|
|
28
|
+
`Opus` e `Softize` são nomes próprios em prosa (`o Opus`, `a Softize`). Tokens técnicos
|
|
29
|
+
preservam a grafia de código: `opus check`, `@softize/opus`, `opus.json` e
|
|
30
|
+
`softize.com.br`.
|
|
31
|
+
|
|
32
|
+
Use sempre o termo de produto, não o nome interno da implementação: `sessão`, `ambiente`,
|
|
33
|
+
`cliente` e `agente`. Se a interface diz “cliente”, outra superfície não deve dizer
|
|
34
|
+
“client” para a mesma entidade.
|
|
35
|
+
|
|
36
|
+
## Papel semântico
|
|
37
|
+
|
|
38
|
+
O componente ou o contrato Opus classifica cada texto antes de a Base aplicar a política.
|
|
39
|
+
Essa classificação decide, por exemplo, se o conteúdo é um fragmento estrutural ou uma
|
|
40
|
+
frase. Não copie as regras da Base para cá.
|
|
41
|
+
|
|
42
|
+
| Superfície Opus | Papel enviado à Base |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `label`, botão de confirmação e filhos de `Button` | `button` |
|
|
45
|
+
| `title`, `CardTitle`, `DialogTitle` | `title` |
|
|
46
|
+
| `description`, `hint`, `help` | `description` ou `helper-text` |
|
|
47
|
+
| `messages.success` e `messages.error` | `success` e `error` |
|
|
48
|
+
| `confirm.message`, `DialogDescription` | `dialog-body` |
|
|
49
|
+
| label de campo, filtro ou opção | `label` ou `menu-item` |
|
|
50
|
+
| placeholder de campo ou busca | `placeholder` |
|
|
51
|
+
| `Select.emptyText` e grupos de opção | `empty-state` e `heading` |
|
|
52
|
+
| confirmação local do `ActionTrigger` | `title`, `dialog-body` e `button` |
|
|
53
|
+
|
|
54
|
+
Copy visual e nome acessível são superfícies cumulativas. Um `aria-label` estático nomeia
|
|
55
|
+
um controle icon-only, mas não torna aceitável nem invisível ao gate um texto visual opaco.
|
|
56
|
+
Em `Select`, mantenha o texto auditável no `label` string; `content`/`triggerLabel` com JSX
|
|
57
|
+
estático e inequívoco também entram no inventário, inclusive com transformações nos
|
|
58
|
+
descendentes. Composição ambígua ou dinâmica faz a geração falhar, pois pode introduzir texto
|
|
59
|
+
visual que o protocolo não representa com segurança.
|
|
60
|
+
`ActionTrigger.itemLabel` é conteúdo de domínio em runtime (o nome do registro alvo), não
|
|
61
|
+
é copy editorial estável; `label` e `confirm.*` são as superfícies editoriais cobertas.
|
|
62
|
+
|
|
63
|
+
O mapeamento completo, o limite de cobertura e os comandos do gate estão em
|
|
64
|
+
`docs/code-style.md` do pacote.
|
|
65
|
+
|
|
66
|
+
## Convenções dos componentes
|
|
67
|
+
|
|
68
|
+
- Placeholders de seleção, simples, buscável ou múltipla, usam
|
|
69
|
+
`Selecione <artigo> <coisa>…`, sempre com o caractere `…`. Declare o placeholder no
|
|
70
|
+
contrato; o default do componente é apenas uma rede de segurança.
|
|
71
|
+
- Exemplos são neutros e duráveis: use Empresa X / Empresa Y e
|
|
72
|
+
`contato@empresa-x.com.br`, nunca um cliente real ou que pareça real.
|
|
73
|
+
- Em campos Opus, quando o termo canônico precisa de explicação persistente, mantenha o
|
|
74
|
+
termo curto em `label` e descreva o efeito em `help`. Por exemplo: `Staff` com
|
|
75
|
+
`Tem acesso ao back-office e a todos os workspaces.`
|
|
76
|
+
- Abreviações só entram quando o espaço do componente realmente exigir. Prefira `mínimo`,
|
|
77
|
+
`máximo` e `configuração`; `Ex.:` é tolerado em placeholder.
|
|
78
|
+
|
|
79
|
+
## Vocabulário de estado
|
|
80
|
+
|
|
81
|
+
Nas superfícies de UI do Opus, use “indisponível” para um recurso que não pode ser usado,
|
|
82
|
+
em vez dos rótulos alarmistas “ERRO” ou “FALHOU”. Endpoint, status HTTP e stack pertencem
|
|
83
|
+
ao console, ao log ou à documentação técnica, não ao texto dos componentes.
|