Skip to content

AI Design Rules — reguli hard pentru agentul de canvas

Sursa de adevăr: tools/ai-design-rules.mjs. Lista completă este injectată în fiecare tur al modelului (prompt de sistem) și id-urile sunt raportate de validare / post_apply_observation.

MarcajCe înseamnă
[ENFORCED]Aplicatorul rescrie sau respinge rezultatul — modelul nu poate produce defectul nici dacă ignoră promptul.
[CHECKED]Validarea o raportează ca problemă blocantă; bucla nu avansează la pagina următoare.
[REQUIRED]Doar instrucțiune de prompt, fără poartă mecanică.
  1. [ENFORCED] RULE.CHROME_FIRST — Leading chrome MUST be the first block of a web page: exactly one of topbar, site-nav or brand-bar. Nothing may sit above it.
  2. [CHECKED] RULE.TITLE_SECOND — Immediately after leading chrome comes exactly one title block: page-header (app/dashboard), hero (public landing), OR auth-gate (auth/login page). Never both page-header and hero. Auth pages use auth-gate as the title — do not also add page-header or hero.
  3. [CHECKED] RULE.AUTH_NO_REDUNDANT_HEADER — FORBIDDEN on a page that contains auth-gate: a co-present page-header or hero. Auth-gate already carries logo, title and description. Auth recipe = brand-bar|minimal topbar → centered auth-gate → optional site-footer. Remove the redundant header.
  4. [ENFORCED] RULE.FOOTER_TIGHT — site-footer must sit immediately under the last content block (row = contentBottom + 2-cell gutter). FORBIDDEN: orphan whitespace above the footer that makes it float mid-board. Empty board BELOW the footer on a fixed 1080 artboard is normal — the defect is the gap ABOVE.
  5. [ENFORCED] RULE.NO_HERO_AT_ROW_0 — A hero or page-header MUST NOT start at row 0 unless it is the only block on the page: it always begins below leading chrome.
  6. [ENFORCED] RULE.CONTENT_FLOW — Content blocks follow in one strictly increasing vertical flow. A later block MUST NOT have a row smaller than an earlier block already placed on the page.
  7. [ENFORCED] RULE.FOOTER_LAST — site-footer and bottom-menu MUST be the LAST blocks of the page, below every content block. If you add content after a footer, the footer moves down — never place content below a footer and never leave a footer mid-stack.
  8. [CHECKED] RULE.FOOTER_ONCE — At most one site-footer and one bottom-menu per page.
  9. [CHECKED] RULE.SINGLETON — At most one each of topbar, site-nav, brand-bar, page-header, hero, auth-gate, site-footer per page. A second one means the content belongs on a NEW page (add_page).
  10. [REQUIRED] RULE.SECTION_HEADER_BEFORE_GRID — A card grid, chart pair or list group is introduced by a section-header directly above it, in the same vertical flow — never beside it.
  11. [CHECKED] RULE.NO_TRAILING_EMPTY_PAGE — Never leave a page with zero blocks. Finish the active page before creating or opening the next one.
  12. [CHECKED] RULE.ONE_PAGE_ONE_JOB — Each page answers ONE job. FORBIDDEN: stacking unrelated admin concerns on one board (e.g. users table + log-retention form). Split into separate pages that match sidenav items (Utilizatori vs Configurare).
  13. [CHECKED] RULE.HEADER_MATCHES_NAV — page-header title MUST match the active sidenav / section job (Utilizatori → „Utilizatori”, not a vague „Administrare” that covers several nav items). Breadcrumb current crumb follows the same label.
  14. [CHECKED] RULE.NO_CROSS_CONCERN_STACK — FORBIDDEN: a list/table page that also carries a settings form (retention, archive, configuration). List pages stay list-only; settings/forms live on their own Configurare page. Constrained forms are OK centered only on settings-only pages.
  15. [CHECKED] RULE.NO_STACKED_TITLES — FORBIDDEN: page-header H1 immediately followed by table/log-journal/activity whose title repeats or translates the same name (e.g. „Jurnal loguri” + „Log journals”). Insert a useful buffer (kpi-row, filter-chips, alert) OR omit/clear the block title so the page-header owns the heading.
  16. [REQUIRED] RULE.LIST_CTA_ON_PAGE_HEADER — On list pages (table under page-header): the primary create/add CTA (Creează / Adaugă / Stație nouă / Vendor nou) MUST live on page-header.actions[] (variant primary, optional icon +). FORBIDDEN: putting that CTA on table.toolbarActionLabel / toolbarActionIcon or using page-header.badge as a substitute for the create button. Table toolbar actions stay for secondary ops (Import, Export, Filtre). Omit table.title when page-header already names the list (RULE.NO_STACKED_TITLES). Recipe: topbar → page-header{title, text?, actions:[{label, variant:‘primary’, icon:’+’}]} → table{title:”, searchable…}.
  17. [REQUIRED] RULE.PAGE_PURPOSE — Before composing, name the page job and follow its recipe (dashboard / journal / alerts / users / settings / auth). Sparse dumps of unrelated blocks are not architect quality.
  1. [ENFORCED] RULE.GRID_CELL — One grid cell = 8px. Every col/row/colSpan/rowSpan is an integer count of cells. The desktop artboard is 1920×1080px = 240×135 cells.
  2. [ENFORCED] RULE.TWELVE_COLUMNS — Content uses a 12-column structural grid. Legal spans are 12, 8, 6, 4, 3 only. Prefer layout:{span:12|8|6|4|3, row:“next”} and let the engine compute cells.
  3. [ENFORCED] RULE.CONTENT_PADDING — Content is inset 4 cells (32px) from the left and right board edges; on a page with a docked sidenav the inset starts after the rail. Chrome (topbar / site-nav / brand-bar / site-footer / bottom-menu) spans edge to edge of the area next to the rail with zero inset.
  4. [ENFORCED] RULE.SHARED_EDGES — Every full row shares the same left edge AND the same right edge as every other row. Column starts of a 3-up card row on a 1920 board with a 240-cell rail-less area are the only ones the engine emits — do not hand-compute alternatives.
  5. [ENFORCED] RULE.ROW_GAP — The vertical gap between two consecutive stacked blocks is EXACTLY 2 cells (16px). Not 3, not 10. row(next) = bottom(previous) + 2.
  6. [ENFORCED] RULE.NO_OVERLAP — FORBIDDEN: two block rectangles that intersect. Overlap is repaired or rejected before it reaches the canvas; never leave an overlap “for later”.
  7. [ENFORCED] RULE.MAX_GAP — FORBIDDEN: an orphan vertical gap larger than 6 cells (48px) beyond the normal 2-cell gutter between consecutive content blocks. A row value that jumps below the current stack bottom is clamped to bottom + 2.
  8. [ENFORCED] RULE.NO_ROW_JUMP — FORBIDDEN: inventing large absolute row values (row: 116, 138, 146…) to “make space”. Omit layout, or use row:“next”. An absolute row is a hint only and is snapped, packed or discarded.
  9. [CHECKED] RULE.DEAD_ROW — FORBIDDEN: a content row that leaves more than 2 cells — never ≥25% of the content width — unused on the right. Rows must sum to 12: 12, 8+4, 6+6, 4+4+4. Exception: a lone compact widget (wallet-card, card, toggle-card, progress-*, deadline-list, donut) stays at its natural span and is centered — that unused margin is intentional, not a dead row.
  10. [ENFORCED] RULE.MAX_ROW_ITEMS — At most 3 panels share one desktop row. A fourth panel opens a new row.
  11. [ENFORCED] RULE.IN_BOUNDS — FORBIDDEN: a rectangle that leaves the artboard (col < 0 or col + colSpan > board columns) or overlaps the sidenav rail.
  12. [ENFORCED] RULE.SIDENAV_RAIL — The sidenav rail is a fixed 30-cell (240px) dock at col 0 on desktop, or a full-width top bar. Never resize the rail per block: a changing rail width shifts every content column and is the classic cause of misaligned card rows.
  13. [ENFORCED] RULE.LAYOUT_IS_A_HINT — When layout is omitted the engine auto-packs. When layout is provided it is a HINT: it is snapped to the 12-column grid, packed into an open row, clamped for gaps, or rejected if invalid. Do not fight the engine with pixel math.
  14. [CHECKED] RULE.ADAPTIVE_VIEWPORTS — Desktop, tablet and phone are ONE shared page — never desktop-only. After placing a non-float block on desktop, also author layouts.tablet and layouts.phone for the SAME id via set_block_layout (viewport:“tablet”|“phone”). Widths: desktop 1920 / tablet 820 / phone 430. Optional foldable (720) may follow tablet. screens[] is Flutter-only, not a responsive substitute.
  15. [CHECKED] RULE.NARROW_STACK — On phone (layouts.phone): every content band is a single full-width column — FORBIDDEN side-by-side content peers on the same row. On tablet (layouts.tablet): at most TWO content panels share a row (pairs OK; 3-up desktop cards must stack or become 2+1). KPI/table/list stay full content width on both.
  1. [ENFORCED] RULE.NATURAL_HEIGHT — rowSpan MUST NOT be smaller than the natural height of the block content (per-kind floor, plus rows for table rows / kpi cards). A short frame clips real content.
  2. [ENFORCED] RULE.NO_GIANT_FRAME — Never inflate rowSpan to reserve space. A frame taller than ~2.5 screens (240 cells) is a layout error, and an oversized frame leaves a hollow interior.
  3. [ENFORCED] RULE.PAIR_SAME_ROW — A pair (8+4 or 6+6) MUST share the same row and be height-equalized to the taller panel. Two panels with different bottoms read as broken even when both rects are legal.
  4. [ENFORCED] RULE.ROW_ALIGN_TOP — Side-by-side panels that belong on one row MUST share the same row (top). Staggered tops (“praguri”) are repaired by snapping onto the partner row.
  5. [ENFORCED] RULE.ROW_EQUAL_HEIGHT — Side-by-side panels on one row MUST share the same rowSpan (equalized to the taller natural height). Unequal bottoms are a layout defect.
  6. [ENFORCED] RULE.CONTENT_WIDTH_CONSISTENCY — Solo full-role content bands stacked vertically MUST share the same left and right content edges. FORBIDDEN: a full-width summary above a narrower left-shifted table, or a centered form island under a full-width list on the same page. Preference: list pages = full content width only; settings-only pages may center a constrained form intentionally; mixed list+settings fails (split pages) rather than centering under a table. Compact widgets (wallet-card / card / …) are NOT full-role bands — see RULE.COMPACT_CARD_WIDTH.
  7. [ENFORCED] RULE.COMPACT_CARD_WIDTH — Compact content widgets (wallet-card, card, toggle-card, progress-rings, progress-bars, deadline-list, donut-chart) keep their natural span (typically 4) and are CENTERED when alone on a desktop row. FORBIDDEN: stretching them edge-to-edge across the content area when the payload is a small balance, profile chip, stat, or single card — that reads as a broken empty strip. Pair with an 8-span peer (8+4) or pack 3-up (4+4+4) when a full row is needed. Full-bleed is reserved for full-role bands (page-header, kpi-row, table, log-journal, search, …) and intentionally full lists (activity alone may fill width). Profile recipe: user-profile (constrained span 6 centered) → wallet-card (span 4 centered) — never wallet span 12.
  8. [ENFORCED] RULE.NO_INNER_SCROLL — Artboard blocks NEVER show an inner vertical scrollbar. Grow rowSpan to natural height (with comfortable padding) instead. Auth-gate signup needs more rows than signin.
  9. [ENFORCED] RULE.DOCKED_SQUARE_EDGES — Docked structural chrome (sidenav, topbar, brand-bar, site-nav, site-footer) uses square outer edges (radius 0) where it meets the artboard or adjacent docked panels. Do not card-ify docked chrome with large corner radius. Floating content cards keep —gds-radius.
  10. [ENFORCED] RULE.DENSE_CONTROL_LIST — Dense control lists (log-routing, log-levels, method-list, action-list, toggle-card rows) keep label↔controls as a scannable cluster — no huge mid-row void. Prefer full content width with trailing space after controls; frame tall enough for all rows + bottom padding.
  11. [CHECKED] RULE.INFO_DENSITY — Ops pages (alerts, journals, users) need purposeful density: status summary → actionable queue. FORBIDDEN: thin pages with sparse strips, large empty interiors, and a lonely narrow table.
  12. [CHECKED] RULE.HERO_HEIGHT — A landing hero needs at least its natural height (≈40 cells / 320px); do not shrink it to a banner strip.
  13. [ENFORCED] RULE.TABLE_HEIGHT — A table frame grows with its rows (~3 cells per record plus header/toolbar chrome). Never fit 20 records into an 8-row frame.
  1. [CHECKED] RULE.LANG_CONSISTENCY — User-facing titles and labels match project meta.lang. FORBIDDEN: English block titles on a Romanian page (e.g. „Log journals” under „Jurnal loguri”).
  2. [ENFORCED] RULE.GLYPH_ICONS — Icons are single monochrome glyph characters from catalog.icons.glyphs (▣ ▦ ☰ ⚙ ◈ ⇄). FORBIDDEN: icon-library names (home, bell, shield, credit-card, lucide:*) — they render as raw text. Omit icon when unsure.
  3. [ENFORCED] RULE.NO_DEBUG_TEXT — FORBIDDEN in user-facing copy: debug hashes, sha256/base64 blobs, ids, “ULTIMUL BLOC”, “TODO”, lorem placeholders, or any internal marker.
  4. [REQUIRED] RULE.PLAUSIBLE_CONTENT — Copy is plausible domain content in the project language (Romanian when lang=ro): real-sounding names, dates, amounts, statuses. No English filler on a Romanian page.
  5. [CHECKED] RULE.NO_INVENTED_FIELDS — Use exactly the keys listed in catalog.fields for that kind. Never invent props, kinds or composite layout components.
  6. [REQUIRED] RULE.CARDS_GRID — For a set of cards emit N separate span-4 card blocks in the same flow so the engine packs them 3-up, or one block that already renders a grid (kpi-row). Never hand-place overlapping card frames at improvised columns.
  7. [CHECKED] RULE.ACCENT_TOKENS — Colour only via accent/global tokens (indigo|emerald|amber|rose|blue|teal). FORBIDDEN: hardcoded hex in block data.
  8. [REQUIRED] RULE.LINKED_PAGES — Navigation targets use target:{pageId:}. Never invent an id from a title.
  1. [CHECKED] RULE.MULTI_VIEWPORT_CONCURRENT — Adaptive web is ONE GdsPage with shared blocks/data. ALWAYS author desktop + tablet + phone together — never desktop-only. Desktop writes block.layout (1920×1080); tablet writes layouts.tablet (820×1180); phone writes layouts.phone (430×932). screens[] is Flutter-only, not a responsive variant. Finish narrow layouts for the current component before adding the next one.
  2. [REQUIRED] RULE.RESPONSIVE_COMPOSITION — Preserve the same reading order across viewports. Desktop uses 12-column pairs (8+4 / 6+6 / 4+4+4). Tablet reduces density and stacks secondary panels; KPI rows become 2×2. Phone is a single full-width column. Sidenav: desktop rail → tablet/foldable icon-rail → phone top app bar. FORBIDDEN: collisions, clip-risk, dead rows, or desktop-width frames left unbroken on narrow viewports.
  3. [REQUIRED] RULE.NARROW_LAYOUTS_EXPLICIT — After a desktop placement passes, call set_block_layout on the SAME block ids with viewport=tablet then viewport=phone (or accept engine seed/repair). Prefer layout:{span:12|8|6|4,row:“next”} hints. Never invent a second page or screen to fake responsiveness.
  1. [ENFORCED] RULE.SMALL_BATCH — Emit at most 2 tool calls per turn about ONE component (select + add|update|fill|layout). A second add_block or a mutation on a different id in the same turn is REJECTED. Never batch two components; finish verify/correct before the next.
  2. [ENFORCED] RULE.ONE_PAGE_AT_A_TIME — The canvas shows exactly one board. Finish and verify the active page before you create or open another. Page advancement is refused while the active page has blocking issues.
  3. [CHECKED] RULE.OBSERVE_THEN_ACT — Read post_apply_observation after every apply. If status is needs-correction, fix THAT component before adding anything new.
  4. [REQUIRED] RULE.CORRECT_WITH_TOOLS — Correct with set_block_layout, update_block, fill_block_fake or remove+replace. Prefer removing an explicit layout over inventing a new absolute row.
  5. [CHECKED] RULE.PAGE_ID_EXPLICIT — Once the project has more than one page, set pageId on every add_block / add_sidenav (or select_page first). Otherwise blocks land on page 1.
  6. [CHECKED] RULE.FINAL_PAGE_AUDIT — Before leaving a page confirm: order = chrome → title → content → footer; overlaps = 0; dead rows = 0; orphan gaps = 0; no clip-risk; every non-float block has layouts.tablet AND layouts.phone (RULE.ADAPTIVE_VIEWPORTS) with phone single-column / tablet ≤2-up (RULE.NARROW_STACK). Never claim success over failed validation.
  7. [REQUIRED] RULE.NO_SILENT_SUCCESS — If a component cannot be made valid within the correction budget, simplify its data or skip it with an explicit warning — do not loop and do not report it as done.

Total: 63 reguli.

Enforcement geometric: projects/gds-core/src/lib/layout-system.ts (packGdsBlock, repairGdsLayout, validateGdsLayout), oglindit server-side în tools/ai-agent-loop.mjs (repairShadowPage, validateShadowPage).