@praxisui/dynamic-form 8.0.0-beta.2 → 8.0.0-beta.20
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/README.md +155 -17
- package/docs/dynamic-form-authoring-document-semantics.md +80 -0
- package/docs/dynamic-form-llm-rule-authoring-guide.md +313 -0
- package/docs/dynamic-form-rules-authoring-plan.md +369 -0
- package/docs/hot-metadata-updates.md +141 -0
- package/docs/layout-items-visual-blocks.md +406 -0
- package/fesm2022/praxisui-dynamic-form.mjs +8607 -3284
- package/index.d.ts +461 -22
- package/package.json +12 -6
- package/src/lib/components/praxis-form-actions/praxis-form-actions.json-api.md +441 -0
- package/src/lib/config-editor/praxis-dynamic-form-config-editor.json-api.md +480 -0
- package/src/lib/filter-form/praxis-filter-form.json-api.md +507 -0
- package/src/lib/json-config-editor/form-json-config-editor.json-api.md +491 -0
- package/src/lib/layout-editor/praxis-layout-editor.json-api.md +432 -0
- package/src/lib/praxis-dynamic-form.json-api.md +955 -0
package/README.md
CHANGED
|
@@ -70,10 +70,11 @@ Peer dependencies (Angular v20):
|
|
|
70
70
|
- `@angular/core` `^20.0.0`
|
|
71
71
|
- `@angular/common` `^20.0.0`
|
|
72
72
|
- `@angular/cdk` `^20.0.0`
|
|
73
|
-
- `@praxisui/core` `^0.0.
|
|
74
|
-
- `@praxisui/
|
|
75
|
-
- `@praxisui/
|
|
76
|
-
- `@praxisui/
|
|
73
|
+
- `@praxisui/core` `^8.0.0-beta.12`
|
|
74
|
+
- `@praxisui/rich-content` `^8.0.0-beta.10`
|
|
75
|
+
- `@praxisui/visual-builder` `^8.0.0-beta.10`
|
|
76
|
+
- `@praxisui/settings-panel` `^8.0.0-beta.10`
|
|
77
|
+
- `@praxisui/cron-builder` `^8.0.0-beta.10`
|
|
77
78
|
|
|
78
79
|
## Quick Start
|
|
79
80
|
|
|
@@ -112,8 +113,16 @@ export class FormDemoComponent {
|
|
|
112
113
|
rows: [
|
|
113
114
|
{
|
|
114
115
|
columns: [
|
|
115
|
-
{
|
|
116
|
-
|
|
116
|
+
{
|
|
117
|
+
id: 'col-fullName',
|
|
118
|
+
items: [{ kind: 'field', id: 'field-fullName', fieldName: 'fullName' }],
|
|
119
|
+
fields: ['fullName'],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: 'col-email',
|
|
123
|
+
items: [{ kind: 'field', id: 'field-email', fieldName: 'email' }],
|
|
124
|
+
fields: ['email'],
|
|
125
|
+
},
|
|
117
126
|
],
|
|
118
127
|
},
|
|
119
128
|
],
|
|
@@ -316,6 +325,19 @@ export class FormEditorLauncherComponent {
|
|
|
316
325
|
|
|
317
326
|
Alternatively, when `enableCustomization` is true, `praxis-dynamic-form` renders a gear button that opens the editor internally.
|
|
318
327
|
|
|
328
|
+
## Agentic Authoring & Manifest
|
|
329
|
+
|
|
330
|
+
`praxis-dynamic-form` supports executable authoring contracts through the `ComponentAuthoringManifest`. This allows AI agents to perform structured, safe, and deterministic edits to the form configuration.
|
|
331
|
+
|
|
332
|
+
- **Manifest:** `PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST`
|
|
333
|
+
- **Editable targets:** `field`, `section`, `row`, `column`, `visualBlock`, `formAction`, `formRule`, `message`, `layoutPlacement`, `localField`, `schemaBackedField`.
|
|
334
|
+
- **Operations:** Covers field property updates, local field lifecycle, layout item manipulation, visual block authoring, rule management, and action configuration declared in `PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST`.
|
|
335
|
+
- **Validation:** Deterministic validators cover ID uniqueness, layout integrity, rich content document validity, local/schema-backed separation, destructive confirmation, and editor round-trip preservation.
|
|
336
|
+
- **Examples/evals:** Fixtures cover local transient fields, schema-backed relabeling, layout moves, visual block add/move, visibility rules, unknown target rejection, destructive confirmation, and save/reopen without drift.
|
|
337
|
+
- **Round-trip:** Preserves editor state and ensures that AI-generated patches are compatible with the visual editor.
|
|
338
|
+
|
|
339
|
+
For more details on authoring manifests, see `docs/ai/agentic-authoring/component-authoring-contracts/README.md`.
|
|
340
|
+
|
|
319
341
|
## API Surface
|
|
320
342
|
|
|
321
343
|
- Components: `PraxisDynamicForm`, `PraxisDynamicFormConfigEditor`, `JsonConfigEditorComponent`, `LayoutEditorComponent`
|
|
@@ -419,8 +441,16 @@ const formConfig: FormConfig = {
|
|
|
419
441
|
rows: [
|
|
420
442
|
{
|
|
421
443
|
columns: [
|
|
422
|
-
{
|
|
423
|
-
|
|
444
|
+
{
|
|
445
|
+
id: 'registration-full-name',
|
|
446
|
+
items: [{ kind: 'field', id: 'field-fullName', fieldName: 'fullName' }],
|
|
447
|
+
fields: ['fullName'],
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
id: 'registration-work-email',
|
|
451
|
+
items: [{ kind: 'field', id: 'field-workEmail', fieldName: 'workEmail' }],
|
|
452
|
+
fields: ['workEmail'],
|
|
453
|
+
},
|
|
424
454
|
],
|
|
425
455
|
},
|
|
426
456
|
],
|
|
@@ -452,10 +482,71 @@ Notas:
|
|
|
452
482
|
|
|
453
483
|
See public exports: `projects/praxis-dynamic-form/src/public-api.ts`.
|
|
454
484
|
|
|
485
|
+
## Blocos visuais em colunas
|
|
486
|
+
|
|
487
|
+
O layout de coluna usa `sections[].rows[].columns[].items[]` como contrato
|
|
488
|
+
canônico ordenado. Itens `kind: 'field'` referenciam
|
|
489
|
+
`fieldMetadata[].name`; itens `kind: 'richContent'` hospedam um
|
|
490
|
+
`RichContentDocument` dentro da coluna e não entram em `fieldMetadata`,
|
|
491
|
+
`formData` ou payload HTTP.
|
|
492
|
+
|
|
493
|
+
`sections[].rows[].columns[].fields[]` permanece aceito como entrada de
|
|
494
|
+
migração e como projeção derivada dos itens `kind: 'field'`, mas não deve ser
|
|
495
|
+
usado para criar blocos visuais.
|
|
496
|
+
|
|
497
|
+
No editor visual, **Campos da API** lista apenas campos existentes em
|
|
498
|
+
`fieldMetadata[]` que ainda não aparecem em `columns[].items[]` como
|
|
499
|
+
`kind: 'field'`. Ao adicionar um campo, o editor reinsere o item em `items[]`
|
|
500
|
+
e mantém `fields[]` sincronizado como projeção; `fieldMetadata[]` não é
|
|
501
|
+
alterado.
|
|
502
|
+
|
|
503
|
+
**Adicionar bloco visual** oferece presets internos como texto, aviso,
|
|
504
|
+
separador, card informativo, `callout`, `keyValueList`, `recordSummary`,
|
|
505
|
+
`disclosure` e `emptyState`. Esses presets são apenas atalhos de authoring que
|
|
506
|
+
geram documentos `praxis.rich-content` válidos para itens `kind: 'richContent'`;
|
|
507
|
+
eles não criam novo `kind`, não criam `FieldMetadata` e não participam do
|
|
508
|
+
payload de submit.
|
|
509
|
+
|
|
510
|
+
O runtime do formulário também passa `hostCapabilities` canônicos para
|
|
511
|
+
`@praxisui/rich-content`, permitindo:
|
|
512
|
+
- `actionButton` despachar ações via a mesma trilha de `customAction`/`globalAction`
|
|
513
|
+
já usada pelo formulário;
|
|
514
|
+
- `requiresCapabilities` resolver capacidades como `form.mode.edit`,
|
|
515
|
+
`form.mode.view`, `form.customization.enabled`, `form.resource.connected` e
|
|
516
|
+
`form.actions.submit`.
|
|
517
|
+
|
|
518
|
+
Exemplo mínimo de uma coluna com campo, bloco visual e projeção `fields[]`:
|
|
519
|
+
|
|
520
|
+
```json
|
|
521
|
+
{
|
|
522
|
+
"items": [
|
|
523
|
+
{
|
|
524
|
+
"kind": "richContent",
|
|
525
|
+
"id": "identity-guidance-block",
|
|
526
|
+
"document": {
|
|
527
|
+
"schemaVersion": 1,
|
|
528
|
+
"nodes": [
|
|
529
|
+
{
|
|
530
|
+
"type": "paragraph",
|
|
531
|
+
"text": "Revise os dados principais antes de salvar."
|
|
532
|
+
}
|
|
533
|
+
]
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
{ "kind": "field", "id": "field-nome", "fieldName": "nome" },
|
|
537
|
+
{ "kind": "field", "id": "field-email", "fieldName": "email" }
|
|
538
|
+
],
|
|
539
|
+
"fields": ["nome", "email"]
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
Guia completo: `projects/praxis-dynamic-form/docs/layout-items-visual-blocks.md`.
|
|
544
|
+
|
|
455
545
|
## Documentacao Tecnica da Lib
|
|
456
546
|
|
|
457
547
|
- `projects/praxis-dynamic-form/src/lib/praxis-dynamic-form.json-api.md`
|
|
458
548
|
- `projects/praxis-dynamic-form/docs/hot-metadata-updates.md`
|
|
549
|
+
- `projects/praxis-dynamic-form/docs/layout-items-visual-blocks.md`
|
|
459
550
|
|
|
460
551
|
## Header de seção com avatar dinâmico
|
|
461
552
|
|
|
@@ -504,25 +595,30 @@ Os paths micro são normalizados para `fieldMetadata[].<prop>` para garantir que
|
|
|
504
595
|
|
|
505
596
|
## Regras de formulário (novo contrato)
|
|
506
597
|
|
|
507
|
-
- Formato: cada regra tem `targetType` (`field | section | action | row | column`), `targets: string[]` (IDs canônicos do alvo), e `effect` com `condition` (`JsonLogicExpression | null`), `properties` e `propertiesWhenFalse`.
|
|
598
|
+
- Formato: cada regra tem `targetType` (`field | section | action | row | column | visualBlock`), `targets: string[]` (IDs canônicos do alvo), e `effect` com `condition` (`JsonLogicExpression | null`), `properties` e `propertiesWhenFalse`.
|
|
599
|
+
- Regra de negócio, política, elegibilidade, validação, compliance ou decisão compartilhada deve ser authorada no fluxo governado de `domain-rules`/`shared_rule_authoring`. `visualBlockGuidance` fica restrito a projeção visual opcional: quando uma decisão já governada precisar explicar impacto na UI, a materialização `form_config` pode gerar `type: "visualBlockGuidance"`, `targetType: "visualBlock"` e `metadata.origin: "llm"` com `metadata.reviewStatus: "pending"`; o editor aceita a projeção, marca como `accepted` e materializa `formRulesState` internamente para round-trip visual.
|
|
508
600
|
- Compatibilidade: regras antigas (`context/targetField`) são migradas para `properties/targets` automaticamente; prefixes legados `section:/action:/row:/column:` continuam sendo normalizados quando representarem alvos tradicionais. Para header actions de seção, o ID canônico é preservado como `section:<sectionId>:header-action:<actionLogicalId>`.
|
|
509
601
|
- Semântica de limpeza: valores `null` em `properties/propertiesWhenFalse` removem o override e retornam ao valor base do layout; ausência mantém o valor base.
|
|
510
602
|
- Whitelist por tipo (somente propriedades a seguir são aplicadas; demais são descartadas e logadas em dev):
|
|
511
603
|
- `field`: `visible`, `required`, `readonly`, `disabled`, `className`, `style`, `label`, `description`, `placeholder`, `hint`, `tooltip`, `prefixIcon`, `suffixIcon`, `prefixText`, `suffixText`, `defaultValue`, `options` (array `{label,value,disabled?}`), `appearance` (`fill|outline`), `color` (`primary|accent|warn`), `floatLabel` (`auto|always|never`), `hintPosition` (`start|end`), `validators` (primitivos por chave).
|
|
512
604
|
- `section`: `visible`, `title`, `description`, `icon`, `sectionHeader` (objeto rico), `headerActions` (ações contextuais do cabeçalho) e tambem subpropriedades tipadas como `sectionHeader.mode`, `sectionHeader.sourceField`, `sectionHeader.initialsSourceField`, `sectionHeader.altField`, `sectionHeader.fallbackIcon`, `sectionHeader.emptyState`, `sectionHeader.size`, `sectionHeader.initialsMaxLength`, `className`, `style`, `collapsible`, `collapsed`, `headerTooltip`, `headerAlign` (`start|center`), `appearance` (`card|plain|step`), `stepLabel`, gaps (`gapBottom`, `titleGapBottom`, `descriptionGapBottom`), cores/tipografia (`titleColor`, `descriptionColor`, `titleStyle`, `descriptionStyle`).
|
|
513
605
|
- `action`: `visible`, `disabled`, `loading`, `label`, `icon`, `tooltip`, `color` (`primary|accent|warn|basic`), `variant` (`raised|stroked|flat|fab`), `size` (`small|medium|large`), `className`, `style`.
|
|
514
|
-
- IDs canônicos de regras para actions do header de seção usam o formato `section:<sectionId>:header-action:<actionLogicalId>`, evitando colisão com botões globais e com ações repetidas em seções diferentes.
|
|
515
|
-
- Se a seção ainda não tiver `id`, o runtime aceita o fallback `header-action:<actionLogicalId>` por compatibilidade; para contratos persistidos, prefira sempre materializar `section.id`.
|
|
516
606
|
- `row`: `visible`, `gap`, `rowGap`, `className`, `style`.
|
|
517
607
|
- `column`: `visible`, `span`, `offset`, `order`, `hidden`, `align` (`start|center|end|stretch`), `padding`, `className`, `style`.
|
|
518
|
-
-
|
|
519
|
-
-
|
|
608
|
+
- `visualBlock`: `visible`, `hidden`, `layout`, `className`, `rootClassName`, `style`, `text`, `title`, `message`; para documentos compostos, use `textNodeId`, `titleNodeId` ou `messageNodeId` para apontar o node textual seguro.
|
|
609
|
+
- IDs canônicos de regras para actions do header de seção usam o formato `section:<sectionId>:header-action:<actionLogicalId>`, evitando colisão com botões globais e com ações repetidas em seções diferentes.
|
|
610
|
+
- Se a seção ainda não tiver `id`, o runtime aceita o fallback `header-action:<actionLogicalId>` por compatibilidade; para contratos persistidos, prefira sempre materializar `section.id`.
|
|
611
|
+
- Runtime (FormRulesService): filtra por whitelist, converte tipos (enum/number/boolean/string), saneia objetos (`options/validators/style`), aplica remoção de chaves com `null` e retorna mapas `fieldProps/sectionProps/actionProps/rowProps/columnProps/visualBlockProps`.
|
|
612
|
+
- Renderização: `PraxisDynamicForm` aplica overrides em campos/seções/ações/linhas/colunas/blocos visuais (visibilidade, gaps, padding, classes, estilos, labels e texto seguro, conforme o tipo); colunas respeitam `align/span/offset/order/hidden/padding` vindo das regras.
|
|
613
|
+
- Regras de `visualBlock` são visual-only: elas não criam `FieldMetadata`, `FormControl`, valor em `formData` ou payload HTTP. Use campo local com `submitPolicy` quando a experiência precisar de valor persistível ou leitura em `rawFormData`.
|
|
520
614
|
|
|
521
615
|
### Builder integrado
|
|
522
616
|
|
|
523
|
-
- No editor visual, use a aba “Propriedades” (integrada ao builder) para selecionar o alvo (`targetType` + autocomplete de IDs de campos/seções/ações/linhas/colunas), escolher propriedades whitelisted e definir valores para `properties` (branch true) e `propertiesWhenFalse` (branch false). Botão “Limpar override” remove a propriedade (equivalente a `null`).
|
|
617
|
+
- No editor visual, use a aba “Propriedades” (integrada ao builder) para selecionar o alvo (`targetType` + autocomplete de IDs de campos/seções/ações/linhas/colunas/blocos visuais), escolher propriedades whitelisted e definir valores para `properties` (branch true) e `propertiesWhenFalse` (branch false). Botão “Limpar override” remove a propriedade (equivalente a `null`).
|
|
618
|
+
- Para `visualBlock`, o painel de propriedades exibe um seletor de node quando a propriedade é `text`, `title` ou `message`; o editor persiste `textNodeId`, `titleNodeId` ou `messageNodeId` automaticamente para evitar sobrescrever o node errado em documentos compostos.
|
|
619
|
+
- No round-trip do builder, `config.type` continua sendo reservado para o tipo visual do node; semântica de regra como `visualBlockGuidance` é preservada internamente em `config.ruleType` e volta para `formRules[].type`.
|
|
524
620
|
- A aba de Propriedades usa inputs tipados (enum/number/boolean/string/JSON) conforme o schema injetado; valores inválidos são ignorados.
|
|
525
|
-
- O config editor fornece `targetSchemas` (campos/seções/ações/linhas/colunas) e `targetPropertySchemas` para o builder; `formRules` são salvas no formato canônico (sem `context/targetField`).
|
|
621
|
+
- O config editor fornece `targetSchemas` (campos/seções/ações/linhas/colunas/blocos visuais) e `targetPropertySchemas` para o builder; `formRules` são salvas no formato canônico (sem `context/targetField`).
|
|
526
622
|
|
|
527
623
|
## Layout padrão (sem FormConfig)
|
|
528
624
|
|
|
@@ -665,14 +761,14 @@ Cobertura ja estabilizada:
|
|
|
665
761
|
- `JSON` com edicao real e bloqueio de payload invalido
|
|
666
762
|
- `Hooks`
|
|
667
763
|
- `Acoes` (`Botoes Padrao` e `Layout`)
|
|
668
|
-
- `Acoes` customizadas profundas (`
|
|
764
|
+
- `Acoes` customizadas profundas (`dialog.alert` com `globalAction` simples e estruturado)
|
|
669
765
|
- `Comportamento`
|
|
670
766
|
- `Mensagens`
|
|
671
767
|
- `Dicas e Tooltips`
|
|
672
768
|
|
|
673
769
|
Ajuste tecnico aplicado durante essa fase:
|
|
674
770
|
- [praxis-dynamic-form-config-editor.ts](/mnt/d/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-dynamic-form/src/lib/config-editor/praxis-dynamic-form-config-editor.ts) agora chama `this.jsonEditor?.updateJsonFromConfig(this.editedConfig)` em `onConfigChange(...)` para manter o editor JSON sincronizado com mudancas vindas de abas como `Hooks`.
|
|
675
|
-
- [actions-editor.component.ts](/mnt/d/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-dynamic-form/src/lib/actions-editor/actions-editor.component.ts) agora
|
|
771
|
+
- [actions-editor.component.ts](/mnt/d/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-dynamic-form/src/lib/actions-editor/actions-editor.component.ts) agora usa o catalogo global canonico do app para o editor de acoes customizadas.
|
|
676
772
|
- [actions-editor.component.ts](/mnt/d/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-dynamic-form/src/lib/actions-editor/actions-editor.component.ts) agora preserva draft local dos campos globais e usa `track` estavel em `actions.custom`.
|
|
677
773
|
|
|
678
774
|
Comando base usado para rerodar suites isoladas:
|
|
@@ -720,6 +816,7 @@ Apache-2.0 – see the `LICENSE` packaged with this library or the repository ro
|
|
|
720
816
|
- Fluxo de Schema (ETag/304, schemaId, reconciliação): `projects/praxis-core/docs/schema-flow.md` (canônico) e `docs/schemas/fluxo-schema.md` (resumo operacional)
|
|
721
817
|
- Guia de implementação e metadados da cascata: `docs/CASCADE-NATIVA.md`
|
|
722
818
|
- Padrões de endpoints (Options vs Filter) para selects: `projects/praxis-dynamic-fields/docs/generic-crud-service.md` (canônico) e `docs/DEVS-GENERIC-CRUD-SERVICE.md` (resumo operacional)
|
|
819
|
+
- Recipe oficial de Entity Lookup corporativo com `RESOURCE_ENTITY`, `dependsOn`, `dependencyFilterMap`, reidratação por `by-ids` e política de seleção: `examples/ai-recipes/praxis-dynamic-form.entity-lookup-procurement.json`
|
|
723
820
|
|
|
724
821
|
## Verificação de Schema (ETag/If-None-Match)
|
|
725
822
|
|
|
@@ -747,3 +844,44 @@ Apache-2.0 – see the `LICENSE` packaged with this library or the repository ro
|
|
|
747
844
|
- Quando `API_URL.default.baseUrl` for relativo (ex.: `'/api'`), a lib resolve a origem a partir de `location.origin` no browser. Isso cobre o cenário comum com proxy de dev (`/api`, `/schemas`).
|
|
748
845
|
- Em SSR (sem `location.origin`), configure `baseUrl` absoluto (ex.: `https://api.acme.com/api`) para evitar erros do tipo “Invalid URL” ao construir chamadas de `/schemas/filtered`.
|
|
749
846
|
- O `GenericCrudService.getSchemasFilteredBaseUrl()` retorna sempre uma URL absoluta; o `SchemaMetadataClient` também aceita `baseUrl` relativo quando há origin disponível.
|
|
847
|
+
|
|
848
|
+
## Regras de Domínio Compartilhadas
|
|
849
|
+
|
|
850
|
+
`praxis-dynamic-form` pode consumir regras materializadas pelo backend sem gravá-las dentro de `FormConfig`. O input `domainRules` é opt-in e combina as regras remotas com `config.formRules` no mesmo `FormRulesService`.
|
|
851
|
+
|
|
852
|
+
```html
|
|
853
|
+
<praxis-dynamic-form
|
|
854
|
+
formId="funcionarios-form-demo"
|
|
855
|
+
[config]="formConfig"
|
|
856
|
+
[domainRules]="{
|
|
857
|
+
enabled: true,
|
|
858
|
+
targetArtifactKey: 'funcionarios-form-demo',
|
|
859
|
+
targetLayer: 'form_config',
|
|
860
|
+
targetArtifactType: 'praxis-dynamic-form',
|
|
861
|
+
status: 'applied'
|
|
862
|
+
}">
|
|
863
|
+
</praxis-dynamic-form>
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
- `targetArtifactKey` usa `formId`/`componentInstanceId` quando omitido.
|
|
867
|
+
- `targetLayer` default: `form_config`.
|
|
868
|
+
- `targetArtifactType` default: `praxis-dynamic-form`.
|
|
869
|
+
- Para consumo runtime, prefira `status: 'applied'`; `pending_review` pertence a etapas de governança antes da aplicação.
|
|
870
|
+
- `materializedPayload` pode ser um `FormLayoutRule` direto ou uma operação reconhecida, como `rule.visualBlockGuidance.add`, desde que essa operação seja tratada como projeção visual derivada e não como fonte primária da regra de negócio.
|
|
871
|
+
- A rastreabilidade da regra compartilhada fica em `metadata.domainRule`.
|
|
872
|
+
- Quando o backend enviar `decisionDiagnostics`, o runtime preserva esse envelope em `metadata.domainRule.decisionDiagnostics`, mantendo a explicação canônica da decisão semântica governada junto da regra derivada.
|
|
873
|
+
|
|
874
|
+
E2E vivo com API real:
|
|
875
|
+
|
|
876
|
+
```bash
|
|
877
|
+
PAX_PROXY_TARGET=https://praxis-api-quickstart.onrender.com \
|
|
878
|
+
node scripts/run-playwright-with-dev-host.js \
|
|
879
|
+
--port 4003 \
|
|
880
|
+
--path /funcionarios-form-demo \
|
|
881
|
+
--spec projects/praxis-dynamic-form/test-dev/e2e/funcionarios-form-demo-domain-rules.playwright.spec.ts
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
Validado em 2026-04-23 contra `praxis-api-quickstart` publicado no Render com
|
|
885
|
+
`praxis-config-starter:0.1.0-rc.22`: o spec confirmou resposta 200 de
|
|
886
|
+
`/api/praxis/config/domain-rules/materializations` e presença de materialização
|
|
887
|
+
`form_config` com `operation: "rule.visualBlockGuidance.add"`.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Dynamic Form Authoring Document Semantics"
|
|
3
|
+
doc_type: "adr"
|
|
4
|
+
component: "praxis-dynamic-form"
|
|
5
|
+
status: "accepted"
|
|
6
|
+
owner: "praxis-ui"
|
|
7
|
+
last_updated: "2026-04-15"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Dynamic Form Authoring Document Semantics
|
|
11
|
+
|
|
12
|
+
## Decision
|
|
13
|
+
|
|
14
|
+
`DynamicFormAuthoringDocument` e o snapshot canônico completo de autoria de `praxis-dynamic-form`.
|
|
15
|
+
|
|
16
|
+
Ao aplicar um documento canônico:
|
|
17
|
+
|
|
18
|
+
- `config` substitui integralmente a configuração anterior.
|
|
19
|
+
- `bindings` substitui integralmente os bindings persistíveis anteriores.
|
|
20
|
+
- ausência de `bindings.mode` implica limpeza do binding persistido e retorno ao modo default efetivo do host.
|
|
21
|
+
- `contextSnapshot` substitui integralmente o contexto autoral persistido anterior.
|
|
22
|
+
- ausência de `backConfig`, `presentation` ou `schemaPrefs` dentro de `contextSnapshot` significa remoção explícita do bloco ausente.
|
|
23
|
+
|
|
24
|
+
Payloads legados ou parciais recebidos por APIs de compatibilidade, como `applyConfigFromAdapter(...)`, permanecem com semântica `merge-compat`: campos não informados devem ser preservados até migração completa para o contrato canônico.
|
|
25
|
+
|
|
26
|
+
Valores de runtime/discovery, como `schemaUrl`, `submitUrl` e `submitMethod`, não fazem parte do `DynamicFormAuthoringDocument`. O editor pode exibi-los apenas como diagnóstico read-only do host atual, mas nunca os persiste em `bindings` ou `contextSnapshot`.
|
|
27
|
+
|
|
28
|
+
## Field Metadata Editor Patches
|
|
29
|
+
|
|
30
|
+
Patches vindos de `@praxisui/metadata-editor` para `fieldMetadata` seguem semantica de JSON Merge Patch no campo editado.
|
|
31
|
+
|
|
32
|
+
Para `entityLookup`, o bloco `fieldMetadata[].optionSource` e aninhado e deve ser mesclado em profundidade. O dynamic-form deve preservar campos existentes, como `key`, `resourcePath`, `valuePropertyPath` e `capabilities.byIds`, quando o editor alterar apenas partes do contrato, como `dependsOn`, `selectionPolicy`, `detail` ou `capabilities.auditSnapshot`.
|
|
33
|
+
|
|
34
|
+
Essa regra evita perda silenciosa do contrato canonico de Entity Lookup durante o fluxo: abrir editor -> aplicar/salvar -> reabrir editor -> runtime consumir.
|
|
35
|
+
|
|
36
|
+
## Consequences
|
|
37
|
+
|
|
38
|
+
- `backConfig`, `presentation` e `schemaPrefs` permanecem fora de `FormConfig`, mas fazem parte oficialmente do documento autoral persistível em `contextSnapshot`.
|
|
39
|
+
- `Apply` e `Save` vindos do editor operam em modo `replace-all`.
|
|
40
|
+
- `reset` do editor limpa todos os modos persistidos do artefato.
|
|
41
|
+
- adapters legados continuam suportados sem limpeza destrutiva de contexto ausente.
|
|
42
|
+
|
|
43
|
+
## Visual Blocks Inside Layout
|
|
44
|
+
|
|
45
|
+
Blocos visuais dentro de seção, linha ou coluna devem seguir a decisão
|
|
46
|
+
canônica registrada em:
|
|
47
|
+
|
|
48
|
+
- `../../../docs/2026-04-dynamic-form-visual-blocks-layout-items-plan.md`
|
|
49
|
+
- `../../../../docs/adr/ADR-0006-dynamic-form-layout-items-visual-blocks.md`
|
|
50
|
+
- `./layout-items-visual-blocks.md`
|
|
51
|
+
- `./dynamic-form-rules-authoring-plan.md`
|
|
52
|
+
|
|
53
|
+
A semântica alvo é `FormColumn.items`, com uma union discriminada entre campo e
|
|
54
|
+
rich content. `FormColumn.fields` pode ser aceito como entrada de migração para
|
|
55
|
+
configurações antigas, mas não deve seguir como segunda fonte de verdade.
|
|
56
|
+
|
|
57
|
+
O editor deve produzir e persistir o documento canônico, e o runtime deve
|
|
58
|
+
consumi-lo sem adaptador oculto. O round-trip esperado é:
|
|
59
|
+
|
|
60
|
+
`abrir editor -> adicionar bloco visual -> aplicar/salvar -> reabrir -> renderizar`
|
|
61
|
+
|
|
62
|
+
Guardrail permanente:
|
|
63
|
+
|
|
64
|
+
- bloco visual não entra em `fieldMetadata`;
|
|
65
|
+
- bloco visual não cria `FormControl`;
|
|
66
|
+
- bloco visual não entra no payload de submit;
|
|
67
|
+
- bloco visual não usa `source`, `transient` ou `submitPolicy`.
|
|
68
|
+
- operações AI para bloco visual devem escrever apenas em
|
|
69
|
+
`sections[].rows[].columns[].items[]`, usando itens `kind: 'richContent'`;
|
|
70
|
+
- `sections[].rows[].columns[].fields[]` é apenas projeção/migração de campos
|
|
71
|
+
e não pode ser usado para representar bloco visual.
|
|
72
|
+
- presets de bloco visual no editor são atalhos internos para criar
|
|
73
|
+
`RichContentDocument` com nodes já suportados por `@praxisui/rich-content`;
|
|
74
|
+
presets não introduzem novo `kind` nem contrato paralelo.
|
|
75
|
+
- "Adicionar campo da API" reinsere somente campos já existentes em
|
|
76
|
+
`fieldMetadata[]` e ausentes de `columns[].items[]`; a operação escreve o
|
|
77
|
+
item `kind: 'field'` em `items[]` e preserva `fieldMetadata[]`.
|
|
78
|
+
|
|
79
|
+
Em fase beta, não vamos deixar legado para trás sem migração, mas também não
|
|
80
|
+
vamos manter legado como contrato paralelo permanente.
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Dynamic Form LLM Rule Authoring Guide"
|
|
3
|
+
slug: "dynamic-form-llm-rule-authoring-guide"
|
|
4
|
+
description: "Guia operacional para criar, explicar, revisar e aplicar regras de Dynamic Form com apoio de LLM."
|
|
5
|
+
doc_type: "guide"
|
|
6
|
+
document_kind: "host-guide"
|
|
7
|
+
category: "architecture"
|
|
8
|
+
audience:
|
|
9
|
+
- "business-analyst"
|
|
10
|
+
- "frontend"
|
|
11
|
+
- "host"
|
|
12
|
+
- "platform-team"
|
|
13
|
+
- "llm-agent"
|
|
14
|
+
level: "intermediate"
|
|
15
|
+
status: "active"
|
|
16
|
+
owner: "praxis-ui"
|
|
17
|
+
tags:
|
|
18
|
+
- "dynamic-form"
|
|
19
|
+
- "rules"
|
|
20
|
+
- "llm"
|
|
21
|
+
- "json-logic"
|
|
22
|
+
- "governance"
|
|
23
|
+
toc: true
|
|
24
|
+
sidebar: true
|
|
25
|
+
reading_time: "10 min"
|
|
26
|
+
last_updated: "2026-04-22"
|
|
27
|
+
source_of_truth:
|
|
28
|
+
- "projects/praxis-dynamic-form/docs/dynamic-form-rules-authoring-plan.md"
|
|
29
|
+
- "projects/praxis-dynamic-form/src/lib/ai/dynamic-form-rule-authoring-context.ts"
|
|
30
|
+
- "projects/praxis-dynamic-form/src/lib/ai/form-ai-capabilities.ts"
|
|
31
|
+
- "projects/praxis-dynamic-form/src/lib/utils/rule-authoring-diagnostics.ts"
|
|
32
|
+
- "projects/praxis-dynamic-form/src/lib/services/form-rules.service.ts"
|
|
33
|
+
- "projects/praxis-core/src/lib/models/form/rule-property.schema.ts"
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
# Dynamic Form LLM Rule Authoring Guide
|
|
37
|
+
|
|
38
|
+
Este guia define como um analista, host corporativo ou agente LLM deve pedir,
|
|
39
|
+
gerar, explicar, revisar e aplicar regras em `praxis-dynamic-form`.
|
|
40
|
+
|
|
41
|
+
O objetivo e tirar a regra do improviso. A LLM nao deve "adivinhar codigo"; ela
|
|
42
|
+
deve operar sobre o vocabulario autoral publicado pelo componente:
|
|
43
|
+
|
|
44
|
+
- `formRules` e o contrato executavel.
|
|
45
|
+
- `formRulesState` e estado interno do builder visual.
|
|
46
|
+
- `fieldMetadata[].name` identifica campos de negocio.
|
|
47
|
+
- `sections[].rows[].columns[].items[]` identifica layout canonico.
|
|
48
|
+
- `RULE_PROPERTY_SCHEMA` define quais propriedades cada alvo aceita.
|
|
49
|
+
- `rule-authoring-diagnostics` e `ruleDiagnosticsChange` fecham o ciclo de
|
|
50
|
+
validacao e explicacao.
|
|
51
|
+
|
|
52
|
+
## Contrato de responsabilidade
|
|
53
|
+
|
|
54
|
+
| Papel | Pode fazer | Nao deve fazer |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| Analista | Descrever intencao, revisar impacto, aceitar ou rejeitar sugestao. | Editar `formRulesState` manualmente. |
|
|
57
|
+
| LLM | Gerar proposta em `formRules`, explicar impacto e corrigir diagnostics. | Escrever JS, handler, funcao ou regra fora de Json Logic. |
|
|
58
|
+
| Editor | Validar alvo, condicao, propriedades e round-trip visual. | Aplicar regra invalida silenciosamente. |
|
|
59
|
+
| Runtime | Aplicar somente propriedades saneadas e emitir diagnostics. | Criar campos, controles ou payload a partir de visual block. |
|
|
60
|
+
|
|
61
|
+
## Contexto minimo que a LLM precisa receber
|
|
62
|
+
|
|
63
|
+
A LLM nao precisa analisar codigo em runtime. Ela precisa receber um pacote de
|
|
64
|
+
conhecimento serializado pelo host/editor:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"component": "praxis-dynamic-form",
|
|
69
|
+
"capabilityCatalogVersion": "v1.11",
|
|
70
|
+
"fields": [
|
|
71
|
+
{ "name": "tipoContrato", "label": "Tipo de contrato", "type": "select" },
|
|
72
|
+
{ "name": "dataFim", "label": "Data fim", "type": "date" }
|
|
73
|
+
],
|
|
74
|
+
"targets": {
|
|
75
|
+
"field": ["tipoContrato", "dataFim"],
|
|
76
|
+
"section": ["dados-contratuais"],
|
|
77
|
+
"action": ["submit", "cancel"],
|
|
78
|
+
"visualBlock": ["aviso-lgpd"]
|
|
79
|
+
},
|
|
80
|
+
"allowedRuleProperties": {
|
|
81
|
+
"field": ["visible", "required", "disabled", "readonly", "label", "hint"],
|
|
82
|
+
"visualBlock": ["visible", "hidden", "text", "title", "message", "rootClassName"]
|
|
83
|
+
},
|
|
84
|
+
"visualBlockNodes": {
|
|
85
|
+
"aviso-lgpd": [
|
|
86
|
+
{ "id": "title", "label": "Aviso LGPD", "kind": "card", "roles": ["title"] },
|
|
87
|
+
{ "id": "message", "label": "Aviso", "kind": "text", "roles": ["text", "message"] }
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
"existingRules": [],
|
|
91
|
+
"diagnostics": []
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Esse pacote deve ser derivado de `FormConfig`, do catalogo de capacidades e dos
|
|
96
|
+
diagnostics atuais. Ele e o "mapa semantico operacional" do formulario.
|
|
97
|
+
|
|
98
|
+
No runtime/editor, use `buildDynamicFormRuleAuthoringContext(config)` para
|
|
99
|
+
montar esse pacote de forma canonica. A funcao publica:
|
|
100
|
+
|
|
101
|
+
- remove campos `formHidden` dos alvos de regra;
|
|
102
|
+
- coleta campos, secoes, linhas, colunas, actions e visual blocks;
|
|
103
|
+
- publica `allowedRuleProperties` a partir de `RULE_PROPERTY_SCHEMA`;
|
|
104
|
+
- publica `visualBlockNodes` com os nos editaveis de cada rich content block
|
|
105
|
+
quando regras puderem alterar `text`, `title` ou `message`;
|
|
106
|
+
- inclui `existingRules` e `diagnostics`;
|
|
107
|
+
- carrega guardrails permanentes para a LLM, incluindo a regra de que
|
|
108
|
+
`formRulesState` e gerado apenas por internals Praxis apos revisao humana.
|
|
109
|
+
|
|
110
|
+
## Prompt recomendado para criar regra
|
|
111
|
+
|
|
112
|
+
Use pedidos com intencao, alvo, condicao, efeito e criterio de aceite:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
Crie uma regra para o formulario de funcionario.
|
|
116
|
+
|
|
117
|
+
Intencao: quando tipoContrato for "temporario", o campo dataFim deve ficar
|
|
118
|
+
obrigatorio e visivel. Quando nao for temporario, dataFim deve ficar opcional.
|
|
119
|
+
|
|
120
|
+
Use somente JSON Logic canonico.
|
|
121
|
+
Use somente alvos existentes no target catalog.
|
|
122
|
+
Nao altere formRulesState.
|
|
123
|
+
Marque metadata.origin como "llm" e metadata.reviewStatus como "pending".
|
|
124
|
+
Explique o impacto antes de aplicar.
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Resposta esperada da LLM:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"formRules": [
|
|
132
|
+
{
|
|
133
|
+
"id": "rule-data-fim-contrato-temporario",
|
|
134
|
+
"name": "Data fim obrigatoria para contrato temporario",
|
|
135
|
+
"description": "Exige data fim quando o contrato do funcionario for temporario.",
|
|
136
|
+
"metadata": {
|
|
137
|
+
"origin": "llm",
|
|
138
|
+
"reviewStatus": "pending"
|
|
139
|
+
},
|
|
140
|
+
"targetType": "field",
|
|
141
|
+
"targets": ["dataFim"],
|
|
142
|
+
"effect": {
|
|
143
|
+
"condition": {
|
|
144
|
+
"==": [{ "var": "tipoContrato" }, "temporario"]
|
|
145
|
+
},
|
|
146
|
+
"properties": {
|
|
147
|
+
"visible": true,
|
|
148
|
+
"required": true
|
|
149
|
+
},
|
|
150
|
+
"propertiesWhenFalse": {
|
|
151
|
+
"required": false
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Prompt para regra LGPD
|
|
160
|
+
|
|
161
|
+
```text
|
|
162
|
+
Crie uma regra LGPD para o formulario.
|
|
163
|
+
|
|
164
|
+
Quando o campo consentimentoLgpd for false, o botao submit deve ficar
|
|
165
|
+
desabilitado e o bloco visual aviso-lgpd deve mostrar a mensagem:
|
|
166
|
+
"Aceite o termo de privacidade para continuar."
|
|
167
|
+
|
|
168
|
+
Use targetType "action" para submit e "visualBlock" para aviso-lgpd.
|
|
169
|
+
Nao coloque aviso-lgpd em fieldMetadata.
|
|
170
|
+
Nao crie FormControl para aviso-lgpd.
|
|
171
|
+
Use JSON Logic canonico.
|
|
172
|
+
Deixe a sugestao pendente de revisao humana.
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Resposta esperada:
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"formRules": [
|
|
180
|
+
{
|
|
181
|
+
"id": "rule-submit-bloqueado-sem-lgpd",
|
|
182
|
+
"name": "Bloquear envio sem consentimento LGPD",
|
|
183
|
+
"metadata": {
|
|
184
|
+
"origin": "llm",
|
|
185
|
+
"reviewStatus": "pending"
|
|
186
|
+
},
|
|
187
|
+
"targetType": "action",
|
|
188
|
+
"targets": ["submit"],
|
|
189
|
+
"effect": {
|
|
190
|
+
"condition": { "===": [{ "var": "consentimentoLgpd" }, false] },
|
|
191
|
+
"properties": { "disabled": true },
|
|
192
|
+
"propertiesWhenFalse": { "disabled": false }
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"id": "rule-aviso-lgpd-sem-consentimento",
|
|
197
|
+
"name": "Aviso LGPD sem consentimento",
|
|
198
|
+
"metadata": {
|
|
199
|
+
"origin": "llm",
|
|
200
|
+
"reviewStatus": "pending"
|
|
201
|
+
},
|
|
202
|
+
"targetType": "visualBlock",
|
|
203
|
+
"targets": ["aviso-lgpd"],
|
|
204
|
+
"effect": {
|
|
205
|
+
"condition": { "===": [{ "var": "consentimentoLgpd" }, false] },
|
|
206
|
+
"properties": {
|
|
207
|
+
"visible": true,
|
|
208
|
+
"message": "Aceite o termo de privacidade para continuar."
|
|
209
|
+
},
|
|
210
|
+
"propertiesWhenFalse": {
|
|
211
|
+
"visible": false
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Prompt para explicar regra existente
|
|
220
|
+
|
|
221
|
+
```text
|
|
222
|
+
Explique esta regra para um analista de negocio.
|
|
223
|
+
|
|
224
|
+
Responda com:
|
|
225
|
+
1. Quando ela dispara.
|
|
226
|
+
2. O que ela altera.
|
|
227
|
+
3. Se afeta payload ou apenas UI.
|
|
228
|
+
4. Riscos de negocio.
|
|
229
|
+
5. Diagnostics que precisam correcao.
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
A explicacao deve traduzir Json Logic para linguagem de dominio e citar os
|
|
233
|
+
alvos por label quando o contexto tiver label disponivel.
|
|
234
|
+
|
|
235
|
+
## Prompt para corrigir diagnostics
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
Corrija as regras abaixo usando os diagnostics do editor.
|
|
239
|
+
|
|
240
|
+
Nao mude a intencao de negocio.
|
|
241
|
+
Se um alvo nao existir, proponha o alvo existente mais proximo e explique.
|
|
242
|
+
Se uma propriedade for rejeitada pelo schema, substitua por uma propriedade
|
|
243
|
+
permitida ou remova com justificativa.
|
|
244
|
+
Mantenha metadata.reviewStatus como "pending".
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Mapeamento operacional:
|
|
248
|
+
|
|
249
|
+
| Diagnostic | Acao esperada da LLM |
|
|
250
|
+
| --- | --- |
|
|
251
|
+
| `missing-targets` | Pedir ou preencher `targets`. |
|
|
252
|
+
| `field-target-not-found` | Trocar por `fieldMetadata[].name` existente. |
|
|
253
|
+
| `target-not-found` | Trocar por alvo existente no mesmo `targetType`. |
|
|
254
|
+
| `invalid-condition` | Reescrever como Json Logic canonico. |
|
|
255
|
+
| `unsupported-property` | Usar propriedade permitida por `RULE_PROPERTY_SCHEMA`. |
|
|
256
|
+
| `unsupported-target-type` | Escolher um `targetType` permitido. |
|
|
257
|
+
| `missing-effect` | Criar `effect.condition` e `effect.properties`. |
|
|
258
|
+
| `rule-evaluation-error` | Simplificar condicao e remover operadores nao suportados. |
|
|
259
|
+
|
|
260
|
+
Quando o diagnostic envolver apenas propriedade rejeitada, valor invalido ou
|
|
261
|
+
valor parcialmente saneado, o editor tambem pode aplicar a acao deterministica
|
|
262
|
+
`Corrigir`. Essa acao usa o mesmo sanitizer central do runtime, remove ou
|
|
263
|
+
coage propriedades conforme `RULE_PROPERTY_SCHEMA` e regenera `formRulesState`.
|
|
264
|
+
Use LLM quando a correcao exigir decisao semantica de dominio, por exemplo
|
|
265
|
+
trocar uma propriedade inexistente por outra intencionalmente equivalente ou
|
|
266
|
+
explicar por que uma regra deve ser removida.
|
|
267
|
+
|
|
268
|
+
## Fluxo de aplicacao seguro
|
|
269
|
+
|
|
270
|
+
1. Host/editor monta o pacote de contexto.
|
|
271
|
+
2. LLM gera uma proposta por operacao autoral ou em `formRules`.
|
|
272
|
+
Para regra de negocio, politica, elegibilidade, validacao, compliance ou
|
|
273
|
+
decisao compartilhada, use `domain-rules`/`shared_rule_authoring` antes de
|
|
274
|
+
materializar `form_config`. Para uma projecao visual derivada de decisao ja
|
|
275
|
+
governada, aceite `operationId: "rule.visualBlockGuidance.add"` com
|
|
276
|
+
`type: "visualBlockGuidance"`, `targetType: "visualBlock"`,
|
|
277
|
+
`context: "notification"` e metadata pendente.
|
|
278
|
+
3. Compilador/adapter rejeita qualquer tentativa de gravar `formRulesState`.
|
|
279
|
+
4. Editor valida alvos, condicao e propriedades.
|
|
280
|
+
5. Editor mostra diff e impacto para o analista.
|
|
281
|
+
6. Analista aceita, edita ou rejeita.
|
|
282
|
+
7. Ao aceitar, editor marca `metadata.reviewStatus: "accepted"`.
|
|
283
|
+
8. Editor tenta gerar `formRulesState` por conversor interno.
|
|
284
|
+
9. Runtime aplica regra e emite `ruleDiagnosticsChange` se houver problema.
|
|
285
|
+
|
|
286
|
+
## Guardrails permanentes
|
|
287
|
+
|
|
288
|
+
- Nunca persistir codigo executavel em regra.
|
|
289
|
+
- Nunca editar `formRulesState` diretamente por LLM.
|
|
290
|
+
- Nunca tentar contornar essa regra no patch final: `compileAiResponse` e
|
|
291
|
+
`applyPatch` tambem rejeitam escrita direta em `formRulesState`.
|
|
292
|
+
- Nunca materializar regra visual direto no estado do builder. O editor preserva
|
|
293
|
+
semantica de `formRules[].type` usando `config.ruleType` internamente no
|
|
294
|
+
round-trip.
|
|
295
|
+
- Nunca criar alvo que nao exista no mapa de targets.
|
|
296
|
+
- Nunca usar propriedade fora do schema permitido.
|
|
297
|
+
- Nunca tratar `visualBlock` como campo.
|
|
298
|
+
- Para `visualBlock` com varios nos editaveis, usar o node id correspondente em
|
|
299
|
+
`textNodeId`, `titleNodeId` ou `messageNodeId`.
|
|
300
|
+
- Nunca esconder erro de regra: todo descarte deve virar diagnostic ou status.
|
|
301
|
+
- Regras LLM entram como `pending`; aceite humano e uma decisao explicita.
|
|
302
|
+
|
|
303
|
+
## Sinal de prontidao para runtime
|
|
304
|
+
|
|
305
|
+
Uma regra esta pronta para runtime quando:
|
|
306
|
+
|
|
307
|
+
- todos os targets existem;
|
|
308
|
+
- a condicao e Json Logic valida;
|
|
309
|
+
- as propriedades passam pelo whitelist;
|
|
310
|
+
- o impacto foi explicado;
|
|
311
|
+
- o preview true/false foi revisado;
|
|
312
|
+
- a origem/revisao esta coerente;
|
|
313
|
+
- o runtime nao emite diagnostic apos aplicar.
|