docs(project-editor): document config schema, form engine, validation

Milestone 6 (final) of the Configuration Engine sprint.

- docs/EDITOR.md: new "Configuration schema, form engine, and validation
  architecture" section covering the field-schema registry, centralized
  validators, live inline feedback, undo/redo, modified-field tracking, and
  pre-publish preview added in M1-M5. Updated the facade signal list and
  folder tour to include schema/.
- ADR-0002 (docs/context/adrs/): records the metadata-augmented-vs-fully-
  schema-driven decision, why severity splits blocking/advisory, and the
  accepted debt (partial [error] binding coverage, schema not yet driving
  template labels).
- FACTS.jsonl (project-editor): decision fact pointing at the ADR.

Note: `barry-cache` is a phantom devDependency (no bin resolves, confirmed in
M1) - ADR/FACTS were authored by hand matching the existing schema/format
rather than via `npm run barry -- adr new` / `validate`.

Gate: tsc --noEmit, npm test (33/33), arch:check, build all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-07-17 09:05:32 +04:00
parent 96b12a1fbe
commit 8d317f043c
3 changed files with 81 additions and 3 deletions

View File

@@ -0,0 +1,42 @@
---
id: ADR-0002
title: Project Editor field-schema registry, centralized validation, and metadata-augmented form engine
status: active
date: 2026-07-16
tags: ["project-editor", "schema", "validation", "undo-redo"]
---
## Context
The Project Editor (`src/app/features/project-editor/`) edits the tenant `BootstrapConfig` across 11 hand-authored section templates, all built on the `shared/ui` field primitives (ADR established post-Sprint 30 redesign; see `docs/EDITOR.md`). Field labels/hints/defaults lived inline per template, `ProjectValidator` issues were not addressable to a field, there was no undo/redo, and no per-field modified/error state. Sprint X+1 ("Configuration Engine & Dynamic Form Foundation") required: a field-schema registry, centralized validation (JSON/CSS/URL/color/locale/duplicate-route/widget-config), live inline validation with publish-gating, pre-publish preview, dirty/modified-field tracking with a leave-warning, and session undo/redo — without duplicating form logic or validators, and without breaking draft/publish/import/export.
## Decision
**Metadata-augmented, not fully schema-driven.** A field-schema registry (`schema/field-schema.model.ts`, `schema/editor-schema.ts`, `schema/editor-schema.service.ts`) declares every editable field (dot-path key, section, type, label/hint keys, default, required, validator refs) as the single source of truth for field identity and validator wiring — but section templates stay hand-authored. The schema drives validation and metadata; it does not render fields. This was chosen over a fully schema-driven renderer because 11 mature templates already exist on top of the `shared/ui` kit, and a renderer rewrite carried materially higher regression risk against "preserve all existing functionality" for no UX gain.
**Validators are pure, composed, and tagged.** `schema/validators/primitives.ts` holds one pure function per concern (hex color, HTTP URL, email, JSON, CSS brace-balance, style-block extraction, route normalization). `ProjectValidator` composes them and attaches `section`, `fieldKey`, and `severity` (`error` | `warning`) to every issue, so the same validator is never re-implemented per field or per section.
**Severity splits blocking from advisory.** `publish()` now gates on `hasBlockingIssues()` (`severity === 'error'`) instead of "any issue exists." All 9 pre-existing checks stayed `error` (no behavior change); the new duplicate-routes and invalid-CSS checks are `warning` — informative, non-blocking, by design.
**Undo/redo is a pure reducer wrapped in debounced facade state.** `schema/history.util.ts` is a framework-free `{past, future}` snapshot reducer (commit/undo/redo, depth-capped). The facade debounces commits (~300ms) so a typing burst collapses into one undo step, and routes undo/redo through the same `localStorage` draft-save path as every other mutation so the autosave never desyncs from the undo stack.
**Modified-field tracking is a schema diff, not a form-state library.** `modifiedFields` walks every schema field and compares current vs. `originalBootstrap` by dot-path — no new dependency, reuses `EditorSchemaService.getByPath`.
## Consequences
Positive:
- One registry answers "what fields exist, what validates them, what do they mean" — new fields register once and get validation + inline-error wiring for free.
- No validator is duplicated: JSON/CSS/color/URL/email logic lives in exactly one place each.
- Zero changes to `ProjectEditorIoService`, `ProjectEditorDraftStorageService`, or the draft/publish/reset flow — full backward compatibility.
- Undo/redo and modified-field tracking added without a state-management library.
Negative / accepted debt:
- Inline `[error]` binding is wired on a subset of fields (theme palette, general name/domain, branding logo) — not yet every schema-backed field across all 11 sections. Section-level visibility (nav badges, save-bar issue list) covers the rest today.
- The field-schema registry is not yet consumed by templates for label/hint rendering (still inline i18n keys in each template) — only for validation, diffing, and change-summary labels. A future pass could fully drive labels from the schema.
- CSS/JSON validators have a thin binding surface today (CSS only via static-page `<style>` blocks; JSON only via import) since no dedicated `customCss`/raw-JSON field exists yet in `BootstrapConfig`.
## Compliance Requirements
- New editable `BootstrapConfig` fields should get a `FieldSchema` entry in `editor-schema.ts` alongside their template addition.
- New validation rules must be added as a pure function in `schema/validators/primitives.ts` and composed into `ProjectValidator` — never inlined ad hoc in a section component.
- `severity: 'error'` is reserved for checks that must block Publish; anything advisory is `'warning'`.

View File

@@ -3,3 +3,4 @@
{"id":"PE-20260713T010000Z-0003","subject":"draft-publish-flow","predicate":"is","object":"client-side only (ProjectEditorFacade.status/dirty/save/publish) because no backend draft/publish endpoint exists yet; PUT /builder/bootstrap/draft and POST /builder/bootstrap/publish are the documented backend gap","src":["docs/Project-Editor.md","src/app/features/project-editor/facade/project-editor.facade.ts"],"status":"active","kind":"constraint","confidence":"high","updated_at":"2026-07-13T01:00:00Z","tags":["project-editor","backend-gap"]}
{"id":"PE-20260713T010000Z-0004","subject":"html-editing","predicate":"uses","object":"MarketplaceHtmlEditorComponent, a contentEditable + toolbar component with no external rich-text dependency; emits raw HTML, never sanitizes during editing","src":["src/app/features/project-editor/components/html-editor/marketplace-html-editor.component.ts"],"status":"active","kind":"decision","confidence":"high","updated_at":"2026-07-13T01:00:00Z","tags":["project-editor","html-editor"]}
{"id":"PE-20260713T010000Z-0005","subject":"navigation-tab","predicate":"supports","object":"header navigation and flat-list footer navigation (add/remove/reorder/edit); grouped-column footer navigation is read-only until a future sprint","src":["src/app/features/project-editor/sections/navigation-section.component.ts"],"status":"active","kind":"constraint","confidence":"high","updated_at":"2026-07-13T01:00:00Z","tags":["project-editor","navigation"]}
{"id":"PE-20260716T220000Z-0006","subject":"config-schema-and-validation","predicate":"is-implemented-by","object":"a field-schema registry (schema/editor-schema.ts, EditorSchemaService) driving centralized, severity-tagged validation (ProjectValidator composing pure schema/validators/primitives functions) and debounced undo/redo (schema/history.util) in ProjectEditorFacade; section templates stay hand-authored (metadata-augmented, not schema-rendered)","src":["docs/context/adrs/ADR-0002-project-editor-config-schema-and-validation-engine.md","src/app/features/project-editor/schema/editor-schema.ts","src/app/features/project-editor/services/project-validator.service.ts","src/app/features/project-editor/facade/project-editor.facade.ts"],"status":"active","kind":"decision","confidence":"high","updated_at":"2026-07-16T22:00:00Z","tags":["project-editor","schema","validation","undo-redo"]}