Appearance
Capabilities Framework — Autonomia da Zelor no Front
Como o agente (Zelor) dirige a SPA: o catálogo declarativo de capabilities que o front publica para a IA e executa quando ela emite uma intent.
Esta doc complementa
code-style.mderouting.md. Em caso de conflito,code-style.mdvence.
O que é uma capability
Uma capability é um ponto de autonomia: uma ação que a Zelor pode executar na UI (navegar para uma tela, preencher um formulário, submeter, destacar um elemento). O front mantém um catálogo dessas capabilities, publica um resumo para a ai-api (via BFF) e, quando a IA escolhe uma e emite uma intent, o front a executa de verdade.
O conceito já existia espalhado em arquivos *.capability.ts por módulo. A arquitetura atual centraliza tudo em src/capabilities/** separando dado de comportamento.
Split dado vs comportamento (a ideia central)
Cada capability tem duas metades de naturezas diferentes:
| Metade | O que é | Onde vive | Serializável? |
|---|---|---|---|
| Manifesto | metadado declarativo: key, rota, label, descrição, parâmetros, superfície | *.yml | Sim |
| Mapper | comportamento: mutar store, abrir modal, submeter | *.mapper.ts | Não (closure ligado a stores/router) |
O manifesto é o que a IA lê para escolher. O mapper é código vivo que toca useXxx() — não cabe em YAML. Por isso são arquivos separados, casados por convenção de path.
Estrutura de pastas
OBRIGATÓRIO: a árvore de src/capabilities/modules/ espelha onde a Zelor toma autonomia — <módulo>/<feature>/<interação>.
src/capabilities/
├── schema.ts # tipos do manifesto + defineCapabilityMapper()
├── loader.ts # resolução de aliases, required, build da capability
├── index.ts # glob dos .yml + .mapper.ts, valida, registra (boot)
└── modules/
└── <módulo>/
└── <feature>/
├── <interação>.yml # manifesto (sempre)
└── <interação>.mapper.ts # comportamento (só se houver)Exemplos reais:
modules/teams/create/create.yml + create.mapper.ts # team.add-user
modules/workflows/list/filters.yml # workflow.filter-status (só navega/filtra)
modules/templates/structure/node/rename.yml + .mapper.ts # template.structure-rename-node| Regra | Descrição |
|---|---|
PROIBIDO registrar capability fora de src/capabilities/ | O sistema antigo (src/modules/*/agente/*.capability.ts) foi 100% removido |
| OBRIGATÓRIO uma capability por ponto de autonomia | Listar, filtrar, criar, editar, excluir são capabilities distintas |
OBRIGATÓRIO .mapper.ts ao lado do .yml de mesmo basename | O loader casa por path; nome divergente não registra comportamento |
Capability só de navegação/filtro pode ser só .yml | Sem apply/submit/highlight, dispensa mapper |
O manifesto (.yml)
yaml
key: workflow.create # identificador único <módulo-singular>.<ação>
module: workflows # módulo dono
route: new-workflow # name da rota no vue-router (tem que existir)
listRoute: list-workflow # opcional: rota de listagem para "passar por" antes
label: Criar workflow # rótulo curto PT-BR
description: Cria um novo workflow de aprovação, define nome e descrição e salva como rascunho
surface: WRITE_API # READ | GUIDED | WRITE_API
actions: [navigate, fillForm, submit]
plan: null # opcional: chave de gating (ex: providerQtd)
params: # só quando há fillForm
- field: name
type: string # string | number | boolean | date | enum
required: true
aliases: [nome, title, titulo]
message: Nome do workflow não informado
- field: description
type: string
required: false
aliases: [descricao]
flow: [workflow.create, workflow.publish] # opcional: sequência multistepCampos
| Campo | Obrigatório | Descrição |
|---|---|---|
key | ✓ | Único no catálogo. Padrão <módulo-singular>.<ação> (workflow.create, template.structure-rename-node). |
module | ✓ | Módulo dono. |
route | ✓ | name da rota no vue-router. Tem que existir — rota inexistente vira denied em runtime. |
listRoute | — | Rota de listagem que o autopilot "visita" antes de navegar (efeito de demo). |
label | ✓ | Rótulo curto, PT-BR. |
description | ✓ | Rica, PT-BR. A IA escolhe a capability por aqui — descreva o efeito e os parâmetros. |
surface | ✓ | Superfície de execução mínima: READ (navegar/filtrar/buscar/destacar), GUIDED (preencher form assistido), WRITE_API (criar/editar/excluir/submeter). |
actions | ✓ | Subconjunto de navigate, fillForm, submit, highlight. |
plan | — | Chave de gating de plano, quando a tela é gateada. Default null. |
params | — | Só com fillForm. Cada um: field, type, required, aliases, message, options (enum). |
flow | — | Sequência ordenada de keys para fluxos multistep. |
O mapper (.mapper.ts)
ts
import { defineCapabilityMapper } from '@/capabilities/schema'
import { useWorkflowEditor } from '@/modules/workflows/hooks/useWorkflowEditor'
export default defineCapabilityMapper({
apply: (values) => {
const editor = useWorkflowEditor()
if (!editor.flowSteps.length) {
editor.initializeSteps()
}
editor.workflowName = values.name as string
if (values.description) {
editor.workflowDescription = values.description
}
return { status: 'ok' }
},
submit: async () => {
const editor = useWorkflowEditor()
await editor.saveWorkflow()
if (!editor.workflowId) {
return { status: 'denied' }
}
return { status: 'ok' }
}
})Contrato
| Slot | Assinatura | Papel |
|---|---|---|
apply | (values) => Result | Aplica os valores: muta store, abre modal, aplica filtro. Chamado pela ação fillForm. |
submit | () => Result | Comita (await store.save()). Chamado pela ação submit. |
highlight | () => void | Só visual. |
validate | (values) => FieldIssue[] | Regras custom (CPF, formato). Devolve issues; vazio = ok. |
Result é { status } com status ∈ { ok, needs_user_input, denied, awaiting_human_confirm, correctable }. Quando needs_user_input, acompanha fields: [{ field, rule, message }].
Regra crítica: valor vai em apply, submit é no-arg
OBRIGATÓRIO: ação que carrega valor (renomear um nó, definir uma condição, escolher um tipo) vai em apply(values) — que o fillForm invoca com os parâmetros já resolvidos. submit() não recebe argumentos; é só o commit final.
ts
// ERRADO — submit não recebe values
submit: (values) => structure.updateNodeTitle(values.nodeId, values.title)
// CERTO — comportamento com valor é apply (e a .yml declara actions:[fillForm] + params)
apply: (values) => structure.updateNodeTitle(values.nodeId as string, values.title as string)O que o loader já faz por você (não repita no mapper)
| Responsabilidade | Quem faz |
|---|---|
Resolver aliases → field e dar trim | Loader (a partir de params) |
Checar required e devolver needs_user_input | Loader (a partir de params) |
| Regras custom (CPF, etc.) | validate no mapper |
| Mutação de estado / commit | apply / submit no mapper |
PROIBIDO rechecar required dentro do mapper — values já chega resolvido e validado.
O loader e a integração com a IA
src/capabilities/index.ts faz o boot no import (chamado por src/main.ts via import '@/capabilities'):
import.meta.glob('./modules/**/*.yml')e('./modules/**/*.mapper.ts')— carrega tudo (eager).- Casa
.yml↔.mapper.tspelo path-base; valida o manifesto. buildAgenteCapability(manifest, mapper)monta o objeto e chamaregisterUICapabilityde@/domain/agente.
YAML é carregado pelo plugin @modyfi/vite-plugin-yaml (registrado em vite.config.ts); *.yml tem declare module em src/types/capabilities-yaml.d.ts.
OBRIGATÓRIO: o framework reusa o runtime existente (registerUICapability, useAgenteDriver, summarizeUICapabilities). O contrato com BFF/ai-api não muda — o front continua publicando apenas { key, routeName }[] em POST sessions/:id/ui/capabilities. O metadado rico (description, params, surface) fica no front para escolha local e documentação.
Parâmetros de rota
navigate monta os params lendo os :tokens do path da rota e buscando cada nome no data da intent. Ou seja: se a rota é integrations/:id, a IA precisa mandar data: { id: '<connectionId>' } — os aliases do manifesto valem para fillForm, não para os params da rota.
A exceção é o companyId, que é contexto do tenant e não algo que a IA descobre: quando ele não vem no data, o driver herda o valor da rota atual (CONTEXT_PARAMS em useAgentDriver). Nenhum outro param é herdado, para não navegar com o id errado.
Ciclo de uma intent
IA escolhe capability → emite intent { capability, action, data }
│
▼
useAgenteDriver.executeIntent
├─ navigate → router.push(route) (passa por listRoute no studio)
├─ fillForm → loader resolve values → required/validate → mapper.apply(values)
├─ submit → mapper.submit()
└─ highlight → mapper.highlight()
│
▼
devolve { status } para a IAComo adicionar uma capability nova
- Identifique o ponto de autonomia e a rota (
name:no router do módulo). - Crie
src/capabilities/modules/<módulo>/<feature>/<interação>.ymlcom o manifesto. - Se houver comportamento, crie o
.mapper.tsde mesmo basename. Leia o hook real antes (src/modules/<m>/hooks/useXxx.ts) e use só membros que existem — nunca invente método. keyúnica; rode os gates:npm run type-check,npm run lint,npm run build.
Regras de code-style (lint estrito)
Os mappers seguem o code-style.md como qualquer arquivo do front:
| Regra | |
|---|---|
| PROIBIDO comentários | Zero comentários nos .yml e .mapper.ts |
PROIBIDO for/while | Usar .map/.filter/.reduce/.forEach |
PROIBIDO try/catch | Deixar o interceptor/exception filter tratar |
PROIBIDO Date cru | Usar @/utils/date |
Imports só via @/ | Sem ../ profundo |
| Aterramento obrigatório | Toda chamada de store/hook tem que existir de verdade |
Funções helper no escopo do módulo do mapper são permitidas (o mapper não é hook nem componente).
Estado atual
113 capabilities em 8 módulos:
| Módulo | Caps | Cobertura |
|---|---|---|
| templates | 57 | create/list/edit/row/status + editor de estrutura (49): node, variable, signer, layout, library, preview, docx |
| revision-workflows | 18 | workflow de aprovação (distinto do builder): lista, filtros, timeline/nodes, condições, reviewer, status, publicar, excluir |
| workflows | 14 | builder de workflow: create/list/edit/steps/status |
| teams | 8 | membros, perfis de permissão, status |
| reports | 7 | create/list/filtros/download/delete |
| contracts | 5 | fluxo multistep: create/workflow/template/parties/review |
| compliance | 2 | create/add-step |
| profile | 2 | idioma, tema |
Relacionados
code-style.md— padrões que os mappers seguemrouting.md— osroute/listRoutereferenciadosstate-management.md— os storesuseXxx()que os mappers consomemtemplates-module.md— módulo de templates / editor de estruturaworkflows-module.md— builder de workflow