@praxisui/dynamic-form 9.0.67 → 9.0.68-rc.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.
@@ -0,0 +1,162 @@
1
+ # Feedback local na troca de registro
2
+
3
+ Estado consolidado em 08/09/2026: feedback local, erro/retry, recuperação de foco
4
+ e ocultação sem transição implementados e validados. As seções de evidência abaixo
5
+ são cronológicas; contagens, limitações e ausência de gravação pertencem à etapa
6
+ descrita, não substituem resultados posteriores. A configuração da página real foi
7
+ aplicada na etapa de [apresentação do cabeçalho](../../praxis-core/docs/widget-header-presentation-followup.md).
8
+ Consulte a [revisão final de documentação e skills](../../praxis-core/docs/widget-identity-documentation-review.md)
9
+ para o estado das orientações canônicas e seus limites de validação.
10
+
11
+ ## Escopo e inventário
12
+
13
+ Mudança transversal pelo consumo do Dynamic Form em páginas, widgets e diálogos,
14
+ sem novos inputs, outputs, opções de editor ou contratos HTTP. Fonte canônica:
15
+ `PraxisDynamicForm.loadEntity`; consumidores continuam usando `resourceId`.
16
+
17
+ A pendência de hidratação, os eventos `loadingStateChange` e o cancelamento da
18
+ leitura já existiam. Aderência: `ja-suportado-mal-nomeado-ou-mal-materializado`
19
+ para feedback; `suportado-parcialmente` para falha e recuperação.
20
+
21
+ ## Comportamento
22
+
23
+ - Durante a leitura, o corpo mantém geometria e componentes montados, mas fica
24
+ totalmente transparente, `inert` e `aria-hidden`. O status local fica fora da
25
+ região marcada `aria-busy`, permitindo anunciar o carregamento.
26
+ - Não usar apenas `visibility:hidden`: campos podem definir `visibility:visible`
27
+ e reaparecer. Essa situação foi detectada e corrigida no navegador real.
28
+ - Erros e respostas inválidas mantêm o corpo e o envio bloqueados. A ação local
29
+ de nova tentativa invalida a deduplicação e lê novamente a identidade atual.
30
+ - O sucesso substitui o snapshot com `form.reset(normalized)`, limpando valores
31
+ ausentes em vez de carregar campos da pessoa anterior. Leituras substituídas
32
+ continuam canceladas; alteração de identidade durante hooks cancela o envio.
33
+ - Criação e snapshots locais authorados por `initialValue` preservam seu fluxo.
34
+
35
+ ## Validação
36
+
37
+ A bateria focal de seleção, runtime/submit, external-config-hydration e
38
+ first-config-hydration passou com 238 testes. Build oficial focal:
39
+ `npm run build:praxis-dynamic-form`, sucesso. Logs locais:
40
+ `/tmp/praxis-entity-read-tests-final.log` e `/tmp/praxis-entity-read-build.log`.
41
+ Após separar o status da região `aria-busy`, os 11 testes de seleção foram
42
+ reexecutados e passaram (`/tmp/praxis-entity-read-a11y-final.log`). Essa execução
43
+ recompilou o template final; o build de pacote precedeu esse último ajuste ARIA.
44
+
45
+ Foram acrescentados testes para ocultação sem desmontar, erro e retry pelo botão,
46
+ bloqueio de submit após erro, resposta parcial, resposta inválida, B tardio após C
47
+ e troca de identidade durante hooks. Os casos existentes cobrem limpar seleção,
48
+ cancelamento e preservação de criação/initialValue. Dois fixtures visuais
49
+ inicialmente falharam por inicialização incompleta; foram corrigidos antes da
50
+ bateria final.
51
+
52
+ CUA em `localhost:4003/page-builder-ia`, sem interceptação de rede: troca real
53
+ entre funcionários, cabeçalho imediato, corpo anterior oculto, status local e
54
+ conteúdo atualizado após sucesso. Conferidos desktop escuro e 390×844 em tema
55
+ claro; árvore de acessibilidade durante loading sem os campos anteriores. Tema,
56
+ viewport e modo de edição restaurados. Nenhum registro ou página remota foi salvo.
57
+ Falha/retry foi validada por teste de componente, sem provocar erro no backend
58
+ real. Não foi realizada sessão com leitor de tela nem publicação de pacotes.
59
+
60
+ ## Artefatos derivados
61
+
62
+ README atualizado. Não houve alteração de public-api, shapes de configuração,
63
+ operações de authoring ou portas: não há novo campo didático no editor nem novo
64
+ link a apresentar. Manifestos AI, registry e corpus HTTP não exigem regeneração.
65
+ Naquela etapa, o guidance instalado de runtime mencionava apenas bloqueio durante pending; a
66
+ família correspondente não está disponível em `codex-skills/` deste checkout
67
+ do monorepo. A sincronização dessa orientação ficou registrada, sem editar só
68
+ a cópia instalada nem criar outra skill paralela. A revisão final localizou a
69
+ fonte em `praxis-codex-skills` e resolveu essa lacuna na skill existente de runtime.
70
+
71
+ ## Prova visual adicional de falha e recuperação
72
+
73
+ O laboratório `/dynamic-page-lab` agora inclui o painel “Validação técnica:
74
+ atualização e recuperação do formulário”. O componente
75
+ `src/app/features/dynamic-page-lab/form-entity-read-proof.component.ts` usa
76
+ `PraxisDynamicForm` e `WidgetShellComponent` reais, com um `HttpBackend` controlado
77
+ e restrito à instância da prova. Não envia HTTP nem modifica registros/configuração.
78
+ Os controles de demonstração completam ou falham a leitura explicitamente;
79
+ não há timers que tornem a inspeção visual dependente da velocidade do operador.
80
+
81
+ Validação via CUA:
82
+
83
+ - Ana carregada → Bruno pendente → falha: cabeçalho Bruno, mensagem local e
84
+ botão de nova tentativa; valores anteriores ausentes da árvore acessível.
85
+ - Enter em “Tentar novamente”: contagem de leituras 2 → 3; após concluir, nome e
86
+ contato são de Bruno. O transporte real da API não foi exercitado neste cenário.
87
+ - Erro em 390×844, tema escuro: mensagem quebra em duas linhas, botão visível,
88
+ sem sobreposição; Tab alcança o elemento seguinte.
89
+ - Bruno → Ana → limpar durante pendência: leitura cancelada, nenhum pedido
90
+ pendente e estado de seleção vazia visível. Foco permaneceu em “Limpar seleção”.
91
+ - O servidor Angular recompilou a demonstração. Não foi reexecutada a bateria
92
+ de runtime nem o build de pacote, pois esta etapa só adicionou o consumidor de
93
+ laboratório e documentação. `resourceId` vazio usa `undefined`, conforme o
94
+ tipo público atual; o runtime normaliza essa ausência para seleção vazia.
95
+
96
+ ### Pendência encontrada: foco após retry
97
+
98
+ Ao acionar “Tentar novamente” por Enter, o botão é removido durante o loading e
99
+ o foco retorna ao documento. A recuperação de dados funciona, mas essa transição
100
+ de foco ainda precisa ser corrigida no Dynamic Form e coberta por teste. O
101
+ critério recomendado é manter orientação dentro do detalhe quando a nova
102
+ tentativa foi explicitamente acionada, sem roubar o foco de quem mudou a seleção
103
+ ou navegou para outro controle durante a leitura. Esta prova não equivale a uma
104
+ certificação completa com leitor de tela.
105
+
106
+ ### Correção e validação do foco
107
+
108
+ A pendência acima foi corrigida no Dynamic Form. A região estável “Dados do
109
+ registro”, com `tabindex=-1`, recebe foco somente na nova tentativa explicitamente
110
+ acionada. Permanece montada no carregamento, erro e sucesso. A resposta não chama
111
+ focus, portanto não retira o foco de quem navegou para outro controle. Uma região
112
+ live persistente anuncia carregamento, erro e sucesso; o texto visual duplicado
113
+ fica fora da árvore acessível. Strings disponíveis em português e inglês.
114
+
115
+ A bateria de runtime/submit/hidratação passou com **240 testes**. Depois do ajuste
116
+ final para evitar texto duplicado, os **13 testes de seleção** foram reexecutados
117
+ e passaram, assim como o build final do pacote. Logs:
118
+ `/tmp/praxis-retry-focus-tests.log`, `/tmp/praxis-retry-focus-final.log` e
119
+ `/tmp/praxis-retry-focus-build-final.log`.
120
+
121
+ CUA confirmou Enter em retry com foco na região de dados no desktop e em 390×844;
122
+ após concluir a leitura, a árvore acessível contém “Dados atualizados” e os valores
123
+ atuais. Preservação do foco até a resposta e não interferência após navegação para
124
+ outro controle foram verificadas com DOM real nos testes de componente. Não houve
125
+ sessão com leitor de tela. Nenhuma configuração remota foi gravada.
126
+
127
+ ## Inventário histórico da evolução de apresentação e integração
128
+
129
+ Inventário inicial, sem novo contrato criado nessa etapa. Vazio, subtítulo
130
+ condicional, formatação e ações estreitas foram posteriormente tratados conforme
131
+ o relatório de apresentação do cabeçalho vinculado acima; a tabela registra a
132
+ decisão anterior à implementação, não pendências atuais.
133
+
134
+ | Necessidade | Aderência e fonte existente | Trabalho restante |
135
+ | --- | --- | --- |
136
+ | Detalhe sem seleção | Já suportado pelo `emptyState` do formulário | Compor a mensagem adequada da página |
137
+ | Cabeçalho vazio mostrando separador | Suportado parcialmente: interpolação de caminhos em Core, sem composição condicional do subtítulo | Auditar materializações/transformações canônicas antes de propor fallback público |
138
+ | Nascimento/CPF no cabeçalho | Suportado parcialmente: `ValuePresentationConfig`/resolver de Core e máscaras de exibição de Dynamic Fields; shell recebe texto | Definir como a apresentação governada chega ao título/subtítulo sem acoplar o shell a funcionário |
139
+ | Ações em largura estreita | Já suportado, mal materializado: shell possui grupos de ações e quebra de linha | Projetar prioridade/overflow no owner Core e conferir editor aberto |
140
+
141
+ Mapa de impacto da próxima evolução: Core (shell/composição), Page Builder
142
+ (editor/capabilities caso haja opção nova), Dynamic Fields (reuso de apresentação),
143
+ consumidor oficial e documentação pública. Qualquer mudança no shape do shell
144
+ exige plano de contrato público, testes de round-trip e revisão de manifestos e
145
+ playgrounds antes da implementação. O inventário acima não declara ainda uma
146
+ lacuna real de contrato.
147
+
148
+ Para integrar esta correção, revisar em conjunto o runtime/template/SCSS/i18n e
149
+ spec de seleção de Dynamic Form, seu README e este documento, mais o componente
150
+ de prova e sua inclusão no Dynamic Page Lab. O checkout contém alterações de
151
+ outras tarefas, inclusive nos mesmos arquivos; não incluir o diretório inteiro
152
+ automaticamente em commit/PR. Nenhum commit, push ou publicação foi executado.
153
+
154
+
155
+ ## Ocultação sem transição entre identidades
156
+
157
+ Durante leitura pendente ou falha, o elemento `form` também recebe `transition:
158
+ none`. A opacidade zero deve ser efetiva no primeiro frame: animações de saída
159
+ não podem manter o registro anterior parcialmente visível sob o novo cabeçalho.
160
+ Ao concluir, o override transitório é removido. `inert`, `aria-hidden` e a reserva
161
+ de espaço continuam ativos durante a espera. A regra pertence ao Dynamic Form,
162
+ não à página consumidora, e não acrescenta configuração pública ou texto novo.
@@ -0,0 +1,114 @@
1
+ # Form layout: Apply, Save and reopen
2
+
3
+ ## Scope and isolation
4
+
5
+ Classification: `docs-apenas` for the repository changes. The operational proof
6
+ reused `/remote-config-storage-persistence-lab` without changing production code,
7
+ routes, providers or contracts. Existing editor documents, configuration storage
8
+ and widget identities already support the journey; no new contract was required.
9
+
10
+ The original `/page-builder-ia` document remained at version 92. Its payload was
11
+ copied through the reference Config API into the existing lab's session identity,
12
+ after a GET confirmed that the target did not exist (404):
13
+
14
+ - Component type: `praxis-dynamic-page`.
15
+ - Component ID: `dynamic-page:remote-canvas-resize-proof:ik=bf5a3dd8-27c8-4aed-b367-afffd680794f`.
16
+ - Lab tenant/user: `demo` / `codex-remote-persistence-lab`, environment `local`.
17
+ - UI tab: 15, opened on the existing remote persistence lab.
18
+
19
+ Every widget key and matching composition/canvas reference was remapped. Form ID,
20
+ table ID and component-instance IDs also received the run UUID. The original
21
+ resource paths, layouts and modes were retained. The setup PUT created version 1;
22
+ it was fixture preparation, not an action performed in the visual editor. All
23
+ subsequent layout edits and saves used the actual UI. This proves the embedded
24
+ Form + DynamicWidgetPage + ApiConfigStorage path, not the complete Page Builder
25
+ host/agentic-document path used by `/page-builder-ia`.
26
+
27
+ ## Observed journey
28
+
29
+ 1. Reloaded the lab and opened its proof. Status became **restaurada**. Selected
30
+ Ayla, opened **Configurar formulário → Layout**.
31
+ 2. Moved Nome Completo right of CPF using the column menu and clicked **Aplicar**.
32
+ The displayed detail changed and the page host reported unsaved changes.
33
+ 3. Moved Nome Completo left again, then cancelled and confirmed **Descartar
34
+ rascunho**. The already-applied CPF → Nome Completo order remained visible and
35
+ was reconstructed when reopening Layout. This exercises opening A → draft B
36
+ → Apply B → draft A → discard A, without treating Apply as a remote save.
37
+ 4. Read the copy through the API: it still exactly matched seeded version 1.
38
+ 5. Moved Email after Telefone in Contato and used **Salvar e fechar** in the Form
39
+ editor. Then used **Salvar layout de teste** in the page host. Status became
40
+ **salva** after the host saved and read back its document.
41
+ 6. A separate GET returned version 2 with a changed ETag. The persisted layout
42
+ contained CPF → Nome Completo and Telefone → Email. The contact-column widths
43
+ travelled with their columns (Telefone 4/12, Email 8/12). Mode remained `view`,
44
+ presentation remained `true`, all five composition links matched the seed,
45
+ and page state remained `{ "values": {} }`.
46
+ 7. Reloaded the browser and reopened the proof. After loading completed, status
47
+ was **restaurada** and the empty-selection instruction appeared. Keyboard
48
+ selection subsequently loaded Ayla. Both edited orders were present in the
49
+ rendered detail and in the reopened Layout editor. A stabilized General tab
50
+ showed **Visualizar** and **Apresentação** checked.
51
+
52
+ Final GETs confirmed that the original was unchanged at version 92 and the copy
53
+ was unchanged at version 2 after reload and selection. No employee write command
54
+ was executed. The copy is retained under the owned identity above for review and
55
+ the selection investigation; it is not a published preset.
56
+
57
+ ## Separate selection observation
58
+
59
+ Do not certify the entire interaction flow as defect-free. After reload, pointer
60
+ actions on the selection radio sometimes produced a visually checked radio while
61
+ the table's selection announcement/action availability and detail remained empty.
62
+ A later snapshot showed radio 194 checked while the status still described record
63
+ 193 deselected with zero selections. A click on the Sol row's text also did not
64
+ replace Ayla's detail. Pressing Space on radio 193 did publish selection and load
65
+ the detail correctly.
66
+
67
+ The saved configuration still enables row-click selection and all five links are
68
+ unchanged. Therefore loss of the persisted link is not supported by the evidence.
69
+ The lab holds page customization enabled; pointer interception, radio native
70
+ activation versus Table selection, and interaction timing remain candidates to
71
+ distinguish. No runtime root cause is established, and no speculative patch was
72
+ made. A transient selector deadline while loading the empty state was followed by
73
+ a snapshot proving that the state was present; it is not evidence of a lost
74
+ empty-state setting. Initial immediate mode snapshots also preceded panel
75
+ rendering; the stabilized General tab confirmed both modes.
76
+
77
+ Follow-up: the radio inconsistency was subsequently reproduced in standalone
78
+ Table DOM tests and corrected at its Material `change` handler. The retained
79
+ copy passed pointer selection, switching records, repeated activation, Space
80
+ clear and row-cell selection; see the
81
+ [radio selection evidence](../../praxis-table/test-dev/radio-pointer-selection-2026-09-08.md).
82
+ This establishes the reproduced radio cause without attributing earlier browser
83
+ crashes to it.
84
+
85
+ The follow-up also found an error in this run's fixture preparation: it had added
86
+ `componentInstanceId` to Rich Content, which does not declare that input. On a
87
+ fresh dev load, metadata validation rejected it. A separate If-Match-protected
88
+ fixture correction removed only this input and advanced the copy to version 3;
89
+ all saved layouts, links and Table/Form identities were preserved. Version 2 is
90
+ the historical UI-save receipt above; version 3 is the corrected retained copy.
91
+ The original page remained unchanged at version 92.
92
+
93
+ ## Evidence and validation limits
94
+
95
+ Read-only receipts/snapshots from this run:
96
+
97
+ - `/tmp/praxis-layout-roundtrip-owned.json`: ownership, initial status and ETag.
98
+ - `/tmp/praxis-layout-roundtrip-seed.json`: version 1.
99
+ - `/tmp/praxis-layout-roundtrip-after-apply.json`: still identical to version 1.
100
+ - `/tmp/praxis-layout-roundtrip-saved.json`: version 2 with both column moves.
101
+ - `/tmp/praxis-layout-roundtrip-reloaded.json`: unchanged version 2.
102
+ - `/tmp/praxis-layout-roundtrip-original-after.json`: unchanged original version 92.
103
+
104
+ Runtime/editor assertions were based on rendered DOM, actual UI actions and
105
+ independent API reads. They do not establish which individual child storage
106
+ requests carried If-Match, enumerate all child config keys, or prove conflict
107
+ handling against deliberately older child preferences. The page version/ETag
108
+ change is confirmed; request-header conformance was not captured in this run.
109
+
110
+ No unit suite or production build was rerun for documentation-only changes. No
111
+ public API, generated manifest, public example, HTTP corpus or skill needed an
112
+ update: `praxis-form-editor-document-roundtrip` already distinguishes Apply,
113
+ component Save, page Save and rehydration. The finding adds concrete evidence,
114
+ not new platform guidance.
@@ -0,0 +1,57 @@
1
+ # Layout toolbar: accessible editor preferences
2
+
3
+ Classification: `local-pequena`. Adherence:
4
+ `ja-suportado-mal-nomeado-ou-mal-materializado`. Dynamic Form already owns density
5
+ and border/separator editor state, Material toggles, CSS token bindings and an
6
+ i18n namespace. No missing contract justified a new input, configuration flag,
7
+ token or persisted setting.
8
+
9
+ The three density controls and two style controls previously had hardcoded
10
+ Portuguese tooltips but no accessible names on their native buttons. The style
11
+ group was also unnamed. The group labels did not distinguish editor preferences
12
+ from changes to the published form.
13
+
14
+ The fix adds translated accessible names to the Material inputs (not only their
15
+ hosts), translates both group labels and the toolbar name, and explains the
16
+ editor-only scope in tooltips. Visible groups are now **Densidade do editor** and
17
+ **Guias do editor**. `compact/cozy/regular` and `border/divider` retain their
18
+ existing state semantics. No CSS, icon source, token or document shape changed.
19
+
20
+ ## Validation
21
+
22
+ - 38 tests passed across `layout-editor-toolbar.spec.ts` and
23
+ `layout-editor.component.spec.ts`. The new cases check native control names in
24
+ pt-BR/en-US, host translator override, selected/pressed states, and preference
25
+ changes without document mutation or configChange emission.
26
+ - The first test compile exposed a fixture translator return-type error; this
27
+ was corrected to use the existing typed host translator contract. Subsequent
28
+ initial-state assertions required a change-detection pass after asynchronous
29
+ ngModel stabilization. The final 38-test run passed.
30
+ - Browser on the official `/page-builder-ia` route, existing tab 8: opened the
31
+ selected employee's Form editor and Layout; verified all five accessible names;
32
+ used Space on Compacta, Bordas das seções and Separadores. States changed while
33
+ **Sem alterações**, disabled Apply and disabled Save remained intact. Restored
34
+ the initial preferences through the same UI.
35
+ - Screenshot review at viewport 1125×912, dark theme, expanded panel and reduced
36
+ panel (720px wide): group labels and controls wrap without overlap; the tooltip
37
+ explains editor-only behavior. This is reduced-panel coverage, not mobile or
38
+ full screen-reader certification. No light-theme screenshot or full AXE audit
39
+ was performed for this naming-only change.
40
+
41
+ Test log: `/tmp/praxis-layout-toolbar-tests-final.log`.
42
+ Build log: `/tmp/praxis-layout-toolbar-build.log`.
43
+ `npm run build:praxis-dynamic-form` completed successfully through the official
44
+ script's dependency/build closure. A final Config API GET confirmed page version
45
+ 92, payload and ETag unchanged; receipt:
46
+ `/tmp/praxis-layout-toolbar-config-after.json`. No page or employee save was used.
47
+
48
+ The README now explains these existing preferences. Authoring manifests and
49
+ public APIs need no change: no authorable path, payload or operation changed.
50
+ Public-site examples, HTTP corpus and generated catalogs do not expose a new
51
+ contract to synchronize. Existing layout/design skills already require named
52
+ icon controls, i18n, host overrides and preservation of drafts; no skill change
53
+ or installation sync is needed for this correction.
54
+
55
+ Other hardcoded copy, the global gap control and broader layout composition
56
+ remain outside this focused fix. This does not certify the whole editor as
57
+ localized or redesign its information architecture.
@@ -0,0 +1,182 @@
1
+ # Master-detail customization: controlled comparison
2
+
3
+ ## Scope
4
+
5
+ Classification: `local-pequena` tests and documentation. Existing widget inputs,
6
+ composition links, authoring document and capabilities represent every scenario;
7
+ no new contract or runtime patch is justified by these results.
8
+
9
+ The reported failure remains open on `/page-builder-ia`. This matrix progressively
10
+ adds factors from that page to the previously passing minimal composition, using
11
+ real Table, Form, widget loader and Page Builder components with controlled HTTP.
12
+
13
+ ## Final result
14
+
15
+ All **8 scenarios passed** in ChromeHeadless:
16
+
17
+ | Scenario | Added factor | Result |
18
+ | --- | --- | --- |
19
+ | 1 | Direct composition runtime with child customization | Pass |
20
+ | 2 | Page Builder host | Pass |
21
+ | 3 | Saved form layout and metadata: 18 fields, 4 sections | Pass |
22
+ | 4 | Builder agentic control enabled | Pass |
23
+ | 5 | Form AI control also enabled | Pass |
24
+ | 6 | Authoring document plus host `pageChange` acknowledgement | Pass |
25
+ | 7 | Saved identity links, transient state, detail shell and canvas | Pass |
26
+ | 8 | Resource schema discovery/reconciliation with `input-first` layout | Pass |
27
+
28
+ Each scenario uses two fictional employees with populated names, dates, monetary
29
+ values, booleans and job/department references. It clicks real table rows and
30
+ checks both child customization inputs, the selected entity, displayed name,
31
+ view/presentation mode, stable loaded component instances during mode changes,
32
+ unchanged authored page JSON and no page/FormConfig saves. It also leaves editing
33
+ while the second entity read is pending and hovers/selects a form section before
34
+ leaving editing. Section selection is cleared on exit.
35
+
36
+ The reference fields issue canonical `jobRole` and `department` option-source
37
+ `options/by-ids` GETs. The test answers these with matching IDs/labels, ignores
38
+ already-cancelled requests and requires settling within a bounded number of
39
+ response rounds. Earlier name-only responses did not exercise these lookups and
40
+ were insufficient to represent a populated employee detail.
41
+
42
+ Log: `/tmp/praxis-master-detail-schema-boundary.log`. The final eight scenarios
43
+ completed successfully (1.906 seconds of test execution). No full
44
+ workspace suite, production build or live LLM request was run. Enabling AI
45
+ controls does not certify backend AI authoring or session transport.
46
+
47
+ ## Fixture provenance and limits
48
+
49
+ `src/lib/test-fixtures/page-builder-employee-form.config.json` takes layout,
50
+ field metadata and editorial block configuration from the version-92 reference
51
+ document. It excludes schema hash/context/verification timestamp and records.
52
+ `page-builder-employee-framing.json` takes its state, five composition links,
53
+ canvas/layout and detail shell; widget keys are mapped to the fixture's `people`
54
+ and `detail`, and the unrelated page-header widget is excluded.
55
+
56
+ These fixtures are test inputs, not new public presets or a second metadata
57
+ source. They intentionally retain field configuration details that a simplified
58
+ handwritten form would omit. Responses are fictional; avatars remain null.
59
+
60
+ `page-builder-employee-response.schema.json` is the response read on September 8
61
+ from the reference API's `/schemas/filtered` for
62
+ `path=/api/human-resources/funcionarios/all&operation=get&schemaType=response`.
63
+ It contains schema metadata and documentation examples, not queried employee
64
+ records. Scenario 8 uses the real SchemaMetadataClient and normalizer, with only
65
+ the `fetch` transport intercepted. Responses use a synthetic ETag and return 304
66
+ when that tag is supplied. The test asserts successful initialization, retained
67
+ authored section IDs, the schema hash, no false outdated warning, and no further
68
+ schema reads during three additional settling turns after the interactions.
69
+
70
+ Initial failures in this extension concerned overly broad persistence assertions,
71
+ not hydration or a reproduced browser crash. The runtime separately persists
72
+ `form-schema-meta:*` evidence/index and `conn:form:*` connection identity with
73
+ `origin: input`. The final assertion checks those keys and payload shapes while
74
+ rejecting page/FormConfig saves. `input-first` and page `autoPersist=false` do not
75
+ mean that every child storage write is disabled. This is observed existing
76
+ behavior, not a new persistence contract. Storage reads still return null in this
77
+ fixture; persisted-child conflict/roundtrip behavior is outside this proof.
78
+
79
+ This matrix does **not** reproduce the full saved-page host: real network timing,
80
+ persisted child configuration loading, the remote table, the app's
81
+ global providers/interceptors/theme, and actual pointer movement
82
+ remain differences. Mouse events in a DOM test cover handlers, not full browser
83
+ hit testing. No factor can be excluded as a cause under every combination merely
84
+ because the controlled scenario passes.
85
+
86
+ ## Earlier browser failure
87
+
88
+ The previously responsive reference tab navigated to `/page-builder-ia` and
89
+ loaded its table. Selecting Ayla and waiting for her name in the detail region
90
+ completed; the next request to access the app switch for keyboard interaction
91
+ timed out in `Runtime.evaluate`. The switch transition was not confirmed. This
92
+ does not establish editing as the exclusive trigger: failure may occur after
93
+ detail hydration, before the next interaction can be performed.
94
+
95
+ Old crashed tabs could not be cleaned up through the browser tool: attempting to
96
+ obtain a crashed tab was blocked by its URL policy for the generated crash page.
97
+ No bypass was attempted and clean browser-process recovery is not claimed.
98
+
99
+ Read-only GET after the comparison returned version 92, structurally identical
100
+ to `/tmp/praxis-draft-rollout-final.json`. The saved page was not modified.
101
+
102
+ ## Follow-up browser journey and next gate
103
+
104
+ The schema gate above is now covered by scenario 8. A fresh inventory confirmed
105
+ four previously failed tabs and one responsive existing tab (8). No new tab was
106
+ opened. In that tab, the following actual UI journey completed:
107
+
108
+ 1. Leave page editing, select reference record 235 and wait for its detail.
109
+ 2. Enable editing and confirm the checked switch in a subsequent snapshot.
110
+ 3. Leave editing, select record 193 (Ayla, also used in the earlier failure),
111
+ wait for detail and confirm that another DOM read succeeds.
112
+ 4. Enable editing, open **Configurar formulário**, and confirm **Visualizar**
113
+ plus **Apresentação** selected in General settings.
114
+ 5. Open **Layout**, inspect its populated sections/fields, cancel without
115
+ changes, and leave editing. Confirm no dialog, an unchecked switch and the
116
+ detail region still present. The tab was left available for visual review.
117
+
118
+ The final read-only configuration GET again matched version 92 exactly. This
119
+ journey did not save a page, edit employee data or modify a layout draft. It
120
+ proves successful interaction in this run; it does not explain previous crashed
121
+ tabs or establish lasting stability. The readonly resolved-runtime panel also
122
+ displayed `legacy CRUD fallback`; that label alone is not evidence that the
123
+ actual schema request fell back and should be checked against transport evidence
124
+ before being used diagnostically.
125
+
126
+ ## Layout edit and cancellation proof
127
+
128
+ The next interactive gate also passed on September 8 in existing tab 8.
129
+ Classification of this follow-up: `docs-apenas`; no production code or test code
130
+ changed. The behavior is `ja-suportado-so-ux`: column movement, draft tracking,
131
+ discard confirmation and the data/presentation modes already have canonical
132
+ implementations. No additional contract was required.
133
+
134
+ Starting with record 193 selected and page editing off, the journey captured the
135
+ detail region's text and the Layout tab's text as comparison baselines. It then:
136
+
137
+ 1. Enabled page editing and opened **Configurar formulário → Layout**.
138
+ 2. Opened **Seção 1 → Linha 2 → Coluna 1 → Opções da coluna** and selected
139
+ **Mover para direita**. The draft then showed CPF before Nome Completo,
140
+ with both columns still at 6/12. Apply and Save became enabled.
141
+ 3. Clicked **Cancelar**. The **Descartar rascunho não salvo** dialog appeared.
142
+ Choosing **Continuar editando** retained the changed field order.
143
+ 4. Cancelled again and chose **Descartar rascunho**. Reopened the same editor:
144
+ the complete Layout tab text matched its initial baseline, including field
145
+ ordering. General settings showed **Sem alterações**, **Visualizar** and
146
+ **Apresentação** selected; the table still had record 193 selected.
147
+ 5. Closed the unchanged editor and left editing. The complete detail-region text
148
+ matched its initial baseline, including the displayed values and their order.
149
+ The responsive tab was left in consultation mode for user review.
150
+
151
+ Read-only page configuration GETs before and after were structurally identical,
152
+ both at version 92 (`/tmp/praxis-layout-cancel-before.json` and
153
+ `/tmp/praxis-layout-cancel-after.json`). No Apply, Save or employee command was
154
+ used. These comparisons establish cancellation behavior for a menu-based column
155
+ move. They do not cover drag hit testing, discard after Apply, browser reload,
156
+ remote save/reopen, or every child storage key. The earlier eight automated
157
+ scenarios were not rerun for this documentation-only follow-up.
158
+
159
+ UX observation: the discard dialog explains that an already-applied preview is
160
+ retained, even in this journey where Apply was never used. That wording describes
161
+ the general behavior but introduces an unused state into the decision. If refined,
162
+ it should derive from the editor's existing applied/draft state and translation
163
+ resources; this run does not justify a new configuration flag or contract.
164
+
165
+ The isolated Apply/Save/reopen gate subsequently completed; see
166
+ [the operational proof](./layout-apply-save-reopen-2026-09-08.md). It confirms both
167
+ column moves, modes and page versioning, while recording a separate pointer
168
+ selection inconsistency after reload. That radio behavior was subsequently
169
+ isolated and corrected; see
170
+ [the Table selection proof](../../praxis-table/test-dev/radio-pointer-selection-2026-09-08.md).
171
+ The eight integration scenarios passed again, with the resource-schema scenario
172
+ now activating Material's touch target. The retained copy also passed live
173
+ pointer/keyboard/row selection after correcting an undeclared Rich Content input
174
+ introduced during fixture preparation. The original version 92 remained intact.
175
+ The remaining broader gate is the complete Page Builder host journey; the lab
176
+ proof does not certify that host or explain earlier browser crashes. Capture any
177
+ recurring failure before changing runtime, without removing customization, draft
178
+ preservation or field configuration to mask it.
179
+
180
+ No public API, generated manifest, HTTP corpus or installed skill changed.
181
+ Existing skills already require canonical sources and exact coverage statements;
182
+ this investigation adds evidence rather than a new runtime rule.
@@ -0,0 +1,74 @@
1
+ # Schema loading: timeout, cancellation and recovery
2
+
3
+ ## Owner and configuration
4
+
5
+ Schema transport policy belongs to Core. Dynamic Form consumes the existing
6
+ `SchemaMetadataClient` through `SCHEMA_METADATA_CLIENT_OPTIONS`; there is no
7
+ widget-specific timeout input or JSON property. A host configures the provider:
8
+
9
+ ```ts
10
+ import { SCHEMA_METADATA_CLIENT_OPTIONS } from '@praxisui/core';
11
+
12
+ { provide: SCHEMA_METADATA_CLIENT_OPTIONS, useValue: { requestTimeoutMs: 15000 } }
13
+ ```
14
+
15
+ The shared Core default is 30,000 ms. An explicit zero disables the deadline;
16
+ cancellation remains available. Core also owns transport error names:
17
+ `TimeoutError` and `AbortError`. The Form legacy Observable schema adapter uses
18
+ the same configured deadline and unsubscribes when cancelled.
19
+
20
+ ## Runtime behavior
21
+
22
+ A schema timeout reaches the existing recoverable initialization error surface.
23
+ The message is localized in the Form catalog. Retry starts a new initialization;
24
+ it does not silently replace the requested `/all` schema with the legacy
25
+ `/filter` schema after a timeout or cancellation.
26
+
27
+ Destroy, retry, mode changes, resource changes and explicit schema URL changes
28
+ cancel the outstanding schema reads. Initialization generations prevent an older
29
+ configuration read, schema result, error handler or finally block from modifying
30
+ the replacement initialization. The saved page layout is retained for retry and
31
+ reconciled with the canonical schema after a successful response.
32
+
33
+ ## Scope and limitations
34
+
35
+ This is transport resilience. During the investigation, the backend returned
36
+ HTTP 200 to the exact schema URL while the internal browser reported
37
+ `ERR_BLOCKED_BY_CLIENT`. A recoverable timeout does not remove that client block
38
+ and must not be reported as correcting its origin.
39
+
40
+ The deadline does not bound all initialization work. Hook execution keeps its
41
+ existing hook-registry policy. Diagnostic schema metadata persistence is not part
42
+ of the transport deadline; a non-transient host with stalled persistence can
43
+ still need separate investigation. Transient layout policy skips those writes.
44
+ No general initialization budget or local business rule was introduced.
45
+
46
+ ## Focused validation
47
+
48
+ `praxis-dynamic-form.schema-loading.spec.ts` covers pending schema → localized
49
+ error → retry, no timeout fallback to a different schema contract, destroy,
50
+ late schema results, and late saved configuration after superseding initialization.
51
+ The fixtures use the existing sections-only `input-first` path so reconciliation
52
+ cannot swallow the timeout and report success with missing field metadata.
53
+ Transport fetch cancellation and concurrent-consumer behavior are tested in Core.
54
+ Live browser and integrated package builds are recorded by the integration review.
55
+
56
+
57
+ The focal schema-loading, external-config-hydration and entity-selection run
58
+ passed 29 tests (`/private/tmp/praxis-form-schema-loading-tests.log`). This run
59
+ includes the error/retry DOM and isolated service fixtures; it does not claim
60
+ live network or production publication validation.
61
+
62
+ ### Non-cooperative custom transports
63
+
64
+ The canonical Core transport reports `AbortError` on cancellation. Two additional
65
+ regressions exercise a transport that ignores cancellation and later rejects with
66
+ a generic error, through both generated-config and host-config initialization.
67
+ The inner construction error handlers validate the initialization generation
68
+ before changing UI state, so the old error cannot stop the newer loading state.
69
+ The late-success and late-error paths are covered independently.
70
+
71
+ The two generic late-error cases failed before the guard (`2 FAILED, 5 SUCCESS`)
72
+ and all seven schema-loading tests passed afterwards. Logs:
73
+ `/private/tmp/praxis-form-late-schema-rejection-red.log` and
74
+ `/private/tmp/praxis-form-late-schema-rejection-green.log`.
@@ -0,0 +1,61 @@
1
+ # Section spacing: canonical eligibility and clear copy
2
+
3
+ Classification: `local-pequena`, owned by Dynamic Form's layout editor.
4
+ Adherence: `ja-suportado-mal-nomeado-ou-mal-materializado`. Core already defines
5
+ `FormSection.gapBottom` as space after the entire section. The Form runtime reads
6
+ that field; no contract, runtime spacing rule, backend or public API changed.
7
+
8
+ The toolbar described column spacing but wrote section `gapBottom`. Eligibility
9
+ depended on a noncanonical `gapCustomized` marker rather than the existing value,
10
+ so an explicitly authored gap without that marker could be overwritten. New
11
+ sections and bulk updates also generated the marker despite the JSON API guide
12
+ warning that it was not stable configuration.
13
+
14
+ The command now fills only absent/null `gapBottom`, preserves explicit values
15
+ (including zero), and emits nothing when no sections are eligible. It no longer
16
+ generates or consults the old marker; unrelated unknown properties in a loaded
17
+ document are not bulk-migrated by this operation. Non-finite proposed numbers
18
+ are rejected. Changing the proposed value alone still does not emit config.
19
+ The existing output-to-input host flow commits the emitted draft.
20
+
21
+ Labels, tooltips and accessible names use the layout editor i18n catalog in
22
+ pt-BR/en-US. **Espaço após as seções** and **Preencher espaços não definidos**
23
+ explain the scope and distinguish this command from editor-only density/guides.
24
+ Existing icons, control styling and theme tokens are retained.
25
+
26
+ ## Browser proof
27
+
28
+ On `/page-builder-ia`, selected Ayla, opened the Form editor and Layout, and
29
+ captured the rendered Layout baseline. Entering 24 kept **Sem alterações** and
30
+ disabled Apply/Save. Invoking **Preencher espaços não definidos** enabled
31
+ Apply/Save and disabled the fill command. Reading the actual JSON editor showed
32
+ the canonical editor envelope with `gapBottom: 24` on the four previously unset
33
+ sections and no `gapCustomized` property.
34
+
35
+ Cancelled, confirmed discard and reopened Layout on the same page. Its full
36
+ rendered text exactly matched the baseline. No Apply, component Save, page Save
37
+ or employee write was performed. Screenshot review covered the dark desktop
38
+ editor, expanded and reduced panels; the new label wrapped without overlap.
39
+ This is not mobile or full screen-reader certification. The runtime effect of
40
+ gapBottom was not newly changed or certified through a persisted save.
41
+
42
+ ## Validation and artifacts
43
+
44
+ The focused toolbar/editor suite adds explicit spacing, zero, fill-once,
45
+ non-finite values, no empty emission and marker-free section creation checks.
46
+ The first compile required a fixture assertion to acknowledge EventEmitter's
47
+ optional value argument; no production typing was relaxed.
48
+ Logs: `/tmp/praxis-section-spacing-tests-final.log` and
49
+ `/tmp/praxis-section-spacing-build.log`.
50
+ Final result: **41 tests passed** and `npm run build:praxis-dynamic-form`
51
+ succeeded through the official dependency/build closure. A final Config API
52
+ GET matched the prior receipt exactly, including version 92 and ETag:
53
+ `/tmp/praxis-section-spacing-config-after.json`. The existing port 4003 server
54
+ remains available; no new server or interactive shell was started.
55
+
56
+ README and the owning JSON API guide were corrected together. The existing Form
57
+ authoring manifest does not declare the removed marker or a separate bulk-fill
58
+ contract; no manifest input, operation or path changed. Public-site examples,
59
+ HTTP corpus and registry generation require no new artifact for this editor
60
+ correction. Existing skills already require canonical layout semantics and
61
+ draft isolation; no skill guidance diverged and no sync was necessary.