# Dynamic Low-Code Platform - AI Instruction and Operating Manual ## 1. Purpose This document defines how AI should think, decide, and produce outputs for this platform. Primary objective: - Maximize delivery through runtime configuration. - Minimize custom code. - Preserve platform consistency, security, and tenant isolation. Primary principle: - Configuration first, code last. Companion documents (this file wins on conflict): | Document | Scope | | --- | --- | | `lowcode.instructions.md` | **Authoring reference**: exact schemas, enum values and worked examples for producing wizard screens, editor scripts, custom components, SQL objects and endpoints. Read it before writing any artifact. | | `lowcode-reference.instructions.md` | **Capability inventory**: field-by-field contract of every ListForm/ListFormField JSON column, chart and pivot options, SQL command hooks, Visual Designer toolbox, the scheduled-mail module and (§12) every ready-made screen under the Saas and Administration menus. Sweep its §0 checklist before producing a screen or component; a request whose answer already exists as a platform screen (§12.5) is configured there, not rebuilt. | | `dotnet.instructions.md` | Binding code standard for everything under `api/` | | `list.instructions.md` | Step-by-step procedure for adding a module / list through seeders | | `../../README.md` | Technical map: what exists, where it lives, how it is wired | | `../../CLAUDE.md` | Operating rules and standing defaults for AI agents in this repo | --- ## 2. Platform Scope Core stack: - Backend: C# .NET 10 (C# 14) + ABP 10.x — kod standardı: `dotnet.instructions.md` - Frontend: React 19 + DevExtreme - Database: SQL Server (dynamic datasource support) - Cache: Redis - Background jobs: Hangfire + ABP Background Workers System nature: - Runtime configurable application engine - Multi-tenant SaaS foundation - Low-code / no-code first --- ## 3. Non-Negotiable Rules 1. Do not propose new custom React component/page development for standard feature requests. 2. Build new screens using platform configuration mechanisms. 3. Every proposal must include tenant and permission design. 4. Never bypass platform authorization patterns. 5. Never hardcode secrets, tenant ids, or connection strings. 6. **Stay inside the framework; ask before stepping out.** The platform may extend itself through its own mechanisms (seed artifacts; adding a missing event, prop, or small backward-compatible capability to an existing UI component — when Developer Kit tooling builds ~70% of the requested component, the rest is completed with such small additions, never with a new component; backward-compatible additions to script recipes / editor options / tokens). Anything beyond that — a new React component or page, a new toolbox family, core engine changes (ListForm runtime, seeders, code generator, auth, DataSourceManager, dispatcher/compiler), new ABP entity/module/migration, platform table schema changes, new NuGet/npm dependencies, `Routes` in `MenusData.json`, new external integrations — is **out of frame**: finish everything that fits inside the frame first, then ask the user with a single question (why configuration is insufficient, the smallest change that works, files touched, rollback) and proceed **only with explicit approval**. Approval is per request and does not carry over. Full lists: `lowcode.instructions.md` §0.8. Exception policy: - Code-level React or backend development can be considered only if user explicitly requests code implementation and configuration path is insufficient. - If exception is required, AI must explain why configuration-based options are not enough. - Never say "cannot be done": the answer is either an in-frame build or an approval question. --- ## 4. Decision Flow (Mandatory) For every request, apply this order: 1. Dynamic configuration with existing ListForm ecosystem 2. SQL Query Manager + Custom Endpoint 3. Dynamic Service 4. Code change (last resort, justification required) --- ## 5. Architecture Guardrails Backend (ABP): - Respect modular boundaries. - Prefer application services over ad-hoc endpoints. - Keep permissions explicit and auditable. Frontend (React + DevExtreme): - Use existing dynamic view infrastructure. - Keep component behavior metadata-driven. - Preserve route, menu, and permission coherence. Data: - SQL-first for shaping, filtering, aggregation, reporting. - Parameterized query patterns only. - Ensure tenant-safe data access. --- ## 6. Dynamic Menu and Routing - Menus are database driven. - Routes are generated/managed dynamically. - Each menu item should map to: - Target component mode - Data source or query context - Endpoint/service target - Permission contract --- ## 7. Dynamic List System Driven by: - ListForm - ListFormFields - ListFormCustomization (UserUiFilter, GridState, ServerJoin, ServerWhere) - ListFormImport and ListFormImportLog - ListFormWorkflow - ListFormJsonRow operations Capabilities: - Dynamic columns, command column, banded columns - Permission-aware visibility and action rendering - Rich filtering, sorting, grouping, paging, searching - Dynamic toolbar actions and URL/dialog/script command flows - Lookup sources: - StaticData - Query - WebService - Cascading lookup parent-child behavior - Dynamic validation, editor options, editor scripts (see 7.1) - Conditional formatting and style injection - Grid state save/load/reset - User filter save/apply/delete flows - Import manager and export flows (xlsx, csv, pdf) - SubForm integration from selected row context - Remote operations and dynamic datasource rebinding Supported editor types: - dxAutocomplete - dxCalendar - dxCheckBox - dxColorBox - dxDateBox - dxDateRangeBox - dxDropDownBox - dxGridBox - dxHtmlEditor - dxLookup - dxNumberBox - dxRadioGroup - dxRangeSlider - dxSelectBox - dxSlider - dxSwitch - dxTagBox - dxTextArea - dxTextBox ### 7.1 Editor Script and Editor Options (builder-backed) `EditorScript` and `EditorOptions` are produced from a shared vocabulary, in two places that must stay identical: | Surface | Location | | --- | --- | | Visual builder (UI) | `ui/src/views/admin/listForm/edit/json-row-operations/editor-script/scriptRecipes.ts` and `.../editor-options/optionSpecs.ts` + `presets.ts` | | Seeder API (C#) | `api/src/Sozsoft.Platform.Domain.Shared/Editors/` — `EditorScript`, `EditorScriptRule`, `EditorScriptCondition`, `EditorOptions`, `EditorOptionsBuilder` | Rules AI must follow: - Prefer a recipe over hand-written JS. Recipes cover arithmetic (`Multiply`, `Subtract`, `Percent`, `Sum`, `Formula`), dates (`Today`, `Days`, `Hours`), data movement (`Copy`, `SetValue`, `Clear`, `ApiToField`), and view/notify actions (`ReadOnly`, `Notify`, `Ask`, `OpenUrl`). Free-form JS is `Custom` and is the last option. - Generated script carries a `// @builder {...}` header on the first line that stores the rules, plus `// @runOnOpen` when any rule runs on form open. **Never edit the header by hand and never emit a script that does not match what the builder would emit** — the dialog then treats the script as manually edited and closes the rule editor. - Rule triggers: `change` (default), `open` (default-value generation), `both`. Conditions join with `and` / `or`. - When a recipe changes on one side, change its port on the other side in the same task. - In seeders write `EditorOptions = EditorOptions.Number(2).ShowClearButton()` style expressions instead of raw JSON strings. Options marked `platform: true` in `optionSpecs.ts` deserialize into typed DTOs (`GridBoxOptionsDto`, `TagBoxOptionsDto`, `ImageUploadOptionsDto`); wrong types are silently ignored. --- ## 8. Dynamic Form System - DevExtreme Form-based runtime field generation - Metadata-driven item groups/tabs - Validation and conditional behavior - CRUD integration - Default value and runtime script handling - Lookup-enabled field model --- ## 9. Dynamic UI Components Supported component families: - DataGrid - PivotGrid - Form - Chart - Scheduler - Gantt - TreeList / Tree view - SubForm tabs (List, Tree, Gantt, Scheduler, Form, Chart) - Widget Group (dashboard KPI cards) - TodoBoard — a Kanban board: columns come from `statusExpr` values, cards change status by drag and drop, new columns (statuses) can be added from the board Runtime UI capabilities: - Per-list layout switching: - Grid - Pivot - Tree - Chart - Gantt - Scheduler - Per-user layout persistence - Lazy loading and view preloading - Shared dynamic datasource pipeline across layouts - Shared filter/search/deep-link behavior - Runtime refresh and query rebind - Runtime state lifecycle: - Save state - Load state - Reset state - Runtime import/export actions from UI flows - Popup and fullscreen editing scenarios - SubForm relation-aware context propagation and tab navigation --- ## 10. Dynamic Component Development Policy Allowed approach: - Configure and bind existing dynamic component infrastructure. Disallowed by default: - New custom React pages/components for normal business requirements. If user requests custom code explicitly: - Provide a warning that low-code path is preferred. - Offer configuration-first alternative first. ### 10.1 Custom Component path (still not hand-written React) When a screen genuinely cannot be expressed as a ListForm, the next step is **not** a physical React page. It is a Custom Component built in the Visual Designer and attached to a menu through the Wizard's `Custom` path. - The component is stored in the database (`Name`, `RoutePath`, `Code`, `Props`, `Dependencies`, `DataSources`) and compiled in the browser with `@babel/standalone`. - The designer document lives in `Props.visualDesigner` **and** in the `/*__SOZSOFT_VISUAL_DESIGNER__…__*/` header of the generated code. `sourceMode` is `visual` or `code`; switching to `code` is one-way — the canvas cannot be restored afterwards, so do not propose it casually. - Toolbox families: `layout` (`PageContainer`, `FlexRow`, `Spacer`), `data` (`Form` — owns the four CRUD endpoints and acts as a container), `platform` (`ListView`, `DataGridView`, `TreeView`, `GanttView`, `TodoBoard`, `CardView`, `SchedulerView`, `PivotView`, `ChartView` — each embeds a ListForm screen through `listFormCode`), `html`/`ui`, and other custom components as `custom`. - **Reuse before drawing:** if the requirement is a list/tree/chart that a ListForm already serves, drop the matching `platform` node with its `listFormCode` instead of rebuilding it out of primitives. - Data binding goes through CRUD endpoints. Filter operators map to the CrudEndpoint `GetList` contract: `eq` is a bare query parameter, every other operator carries its name as a suffix (`?Name.contains=…`). - Permissions are two-layered and mandatory: node-level `designerPermission` for visibility, and per-command Form permissions (`selectPermission`, `insertPermission`, `updatePermission`, `deletePermission`). In `Otomatik` mode the base is the read permission the Wizard created for the component and the commands get `''` / `.Create` / `.Update` / `.Delete` suffixes. - List screen permissions: `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete,.Export,.Import}`. - Every save/delete is mirrored into `configs/seeds/{scope}/data/App.DeveloperKit.CustomComponents.json`; the endpoints it uses are referenced through `crud/{Entity}.json`. A component that is not seed-backed is not done. ### 10.2 SQL View Designer For shaping data before a screen binds to it, prefer a designed view over an inline query: `SqlViewDesignerDialog` produces T-SQL from a diagram (sources, joins, `CROSS/OUTER APPLY`, derived subqueries) plus an SSMS-style criteria grid (Column / Alias / Output / Group By / Sort / Filter / Or…). It is **one-way** — model to T-SQL. A view that does not parse back into the canonical shape drops the dialog to raw SQL mode, so hand-editing a designed view costs the designer. Aggregations available in the Group By column: `GroupBy`, `Where`, `SUM`, `COUNT`, `COUNT_DISTINCT`, `AVG`, `MIN`, `MAX`. Persistence: only the **table designer's** deploy writes a seed file automatically (`{sql|postgres}/object/{TableName}.sql`, full CREATE script, via `save-table-script`). View Designer deploys and any DDL run from the SQL editor (views, procedures, functions) reach the database only — their seed files under `{sql|postgres}/object/` must be written explicitly. Full mechanics and the SP/function production patterns: `lowcode.instructions.md` §8.1–§8.2.1 and §9.5. --- ## 11. Integrations Notification channels: - Sms - Mail - Rocket - Desktop - UiActivity - UiToast - WhatsApp - Telegram Sender module: - Email (ABP Emailing + Amazon SES) - SMS (ABP Sms + Posta Guvercini) - Rocket.Chat (HTTP API) - WhatsApp (HTTP API, template-based) Notification routing model: - Type + Channel routing - Recipient targeting: - All - User - Role - OrganizationUnit - Custom MailQueue: - Template-based body generation - Attachment lifecycle - File outputs (PDF, XLS, TXT) - Queue execution and logging AI workflow integration: - n8n webhook chat endpoint - LangChain agent flow + memory window - Gemini chat model connector --- ## 12. Background Processing Supported engines: - Hangfire - ABP Background Workers Typical use cases: - Scheduled SQL execution - Notification dispatching - Email and integration automation --- ## 13. Security and Compliance Rules - Enforce RBAC and permission-driven visibility at all layers. - Always include permission definitions in feature design. - Never output real credentials, tokens, keys, or secrets. - Use placeholders in examples. - Maintain tenant isolation in every query and action. --- ## 14. Required AI Output Contract For each implementation proposal, AI output must include: 1. Goal 2. Decision flow result (which step used) 3. Artifacts to configure 4. SQL/query/endpoint design (if needed) 5. Menu + route + component mapping 6. Permission and role mapping 7. Tenant isolation notes 8. Validation and test checklist 9. Rollback strategy --- ## 15. Quality Gate Checklist Before finalizing any answer, verify all: - No unnecessary custom code suggestion - No forbidden React component proposal - Tenant awareness is explicit - Permission strategy is explicit - Menu-route binding is explicit - Data safety and SQL parameterization considered - Background processing considered when async/scheduled --- ## 16. Anti-Patterns (Do Not Suggest) - Writing controllers for flows already covered by dynamic infrastructure - Hardcoded endpoint mapping without permission model - Static UI screens disconnected from list/menu/route model - Direct non-tenant-safe query patterns - Copying secrets into examples --- ## 17. Example Request Types AI Must Handle - Create customer list screen - Build purchase module - Create dashboard with charts - Generate reporting screen - Add approval workflow - Add integration-triggered notification process --- ## 18. Escalation Strategy When configuration cannot satisfy requirement: 1. Try SQL-based design 2. Try Custom Endpoint 3. Try Dynamic Service 4. Propose minimal code change with explicit justification — this step is out of frame: **ask and wait for approval before touching code** (rule 3.6). --- ## 19. Final Rule This platform is not a traditional code-first app. It is a runtime configurable low-code engine. AI must optimize for platform consistency, configurability, auditability, and safe scale. --- ## 20. Request Classification Matrix Before proposing any solution, classify the request into one of these classes: Class A - Pure configuration: - Menu, route, list/form fields, validation, permissions, layout, filters - Expected output: configuration-first plan only Class B - Configuration + SQL: - Reporting, complex filtering, aggregation, lookup dependencies - Expected output: configuration + SQL + endpoint mapping Class C - Configuration + Integration: - Notification, sender, queue, webhook, workflow automation - Expected output: configuration + integration contract + retry/fallback notes Class D - Escalated code path: - Only when A/B/C cannot satisfy requirement - Expected output: explicit justification, minimal scope code plan, rollback plan --- ## 21. Response Playbooks ### 21.1 New Screen Playbook 1. Define business objective and actor roles. 2. Define ListForm and ListFormFields structure. 3. Define menu entry and route mapping. 4. Define permissions (R/C/U/D/E) and role mapping. 5. Define datasource and query strategy. 6. Define runtime filters, lookup, validations. 7. Define import/export and state requirements. 8. Define acceptance checklist. ### 21.2 Workflow/Approval Playbook 1. Define states and transition rules. 2. Define transition permissions by role. 3. Define transition side-effects (notification, queue, endpoint call). 4. Define audit log and failure handling. 5. Define rollback/compensation behavior. ### 21.3 Report/Dashboard Playbook 1. Define KPIs and dimensions. 2. Define SQL shaping and aggregation logic. 3. Define chart/pivot configuration. 4. Define date/tenant filters. 5. Define export needs and performance constraints. --- ## 22. Mandatory Acceptance Criteria Template Every final proposal must include a clear, testable acceptance list: 1. Feature can be enabled via configuration. 2. Tenant boundaries are preserved. 3. Required permissions block unauthorized access. 4. Menu-route-screen path is navigable. 5. Data operations are parameterized and safe. 6. Runtime filters and state behaviors work. 7. Integration events (if any) are observable and retry-safe. 8. No unnecessary custom code introduced. --- ## 23. AI Output Templates ### 23.1 Configuration-only Output Template 1. Goal 2. Configuration artifacts 3. Menu/route mapping 4. Permission mapping 5. Validation checklist ### 23.2 Configuration + SQL Output Template 1. Goal 2. Required configuration artifacts 3. SQL design (inputs, outputs, filter parameters) 4. Endpoint/service mapping 5. Security and tenant notes 6. Validation checklist ### 23.3 Escalated Code Output Template 1. Why configuration is insufficient 2. Minimal code scope 3. Compatibility with existing dynamic architecture 4. Migration and rollback plan 5. Risk and test plan --- ## 24. Performance and Scalability Rules - Prefer server-side filtering/sorting/paging for large datasets. - Avoid unbounded result sets in dynamic queries. - Use indexing-aware query patterns for reporting screens. - Keep chart/pivot queries aggregation-focused. - Separate interactive screen queries from heavy export queries. - Use background jobs for long-running batch operations. --- ## 25. Observability and Audit Rules - Log key operations at list/form/integration boundaries. - Preserve who/when/what audit metadata for critical changes. - Capture integration failures with retry reason and payload context. - Keep user-facing messages simple, logs detailed. - Ensure state changes are diagnosable for support teams. --- ## 26. Error Handling Policy - Never expose raw secrets or internal stack details to end users. - Return actionable, localized, user-level messages. - Keep technical diagnostics in logs. - For integration failures: - Retry where safe - Record dead-letter/failure state - Provide manual replay strategy when needed --- ## 27. Change Management Policy - Prefer additive changes over breaking modifications. - Keep existing ListForm contracts backward compatible where possible. - Document behavior changes in rollout notes. - For destructive changes, require explicit migration path. --- ## 28. Definition of Done for AI Proposals A proposal is complete only if all are present: 1. Decision flow step selected and justified. 2. Configuration artifacts clearly listed. 3. Permission and tenant design included. 4. Menu-route-component mapping included. 5. Validation checklist included. 6. Risk/rollback notes included. --- ## 29. Prohibited Suggestion Set AI must not suggest: - Quick custom React page as first solution - Hardcoded tenant ids or environment secrets - Direct SQL string concatenation with user input - Endpoint exposure without permission definitions - Bypassing platform menu-route model for business screens --- ## 30. Final Governance Statement This manual is authoritative for AI behavior in this repository. When in doubt, AI must choose the path that preserves: - Low-code configurability - Tenant safety - Permission correctness - Operational auditability - Long-term maintainability --- ## 31. Seeder-Driven Low-Code Development Guide (Authoritative) There are **two seed surfaces**. AI must not mix them. **A. Repository seeds (shipped with code)** — `api/src/Sozsoft.Platform.DbMigrator/Migrations/`. Platform-owned screens, menus, permissions and language texts. Changing them requires a build and a deploy. AI must learn and teach implementation flow primarily from these assets: - ListFormSeeder_Saas.cs / ListFormSeeder_Administration.cs / ListFormSeeder_{Modul}.cs - MenusData.json - PermissionsData.json - HostData.json - LanguagesData.json - WizardDataSeeder.cs / CrudDataSeeder.cs — the seeders that read surface B below - ListFormSeedDataApplier, CrudEndpointSeedApplier (Domain/Data/) — the single home of the apply rules; both the migrate/seed flow and the wizard import's completion call the same services. `data/` rows are inserted-only on migrate (a possibly stale file must not overwrite live data); `crud/` entries are timestamp-guarded — an existing record is updated or deleted only when the file's `GeneratedAt` is newer than the record's `LastModificationTime`/`CreationTime`. **Migrate adds; it does not overwrite.** Follow this rule in any new seeder. **B. Runtime seeds (written by the running application)** — `configs/seeds/`, resolved through `SeedPathResolver`. The Wizard, the Custom Components screen / Visual Designer, the CRUD Endpoint Manager and every ListForm screen with a `SeedFilePath` write here; the seeders above restore them when the database is recreated. Scope folders mirror the CDN layout: ``` configs/seeds/host/{wizard,data,crud,sql/{object,execute},postgres/{object,execute}} configs/seeds/tenants/{tenantId}/… same layout, tenant scope configs/seeds/.imports/{importId}/… wizard import staging + backup (not scanned) ``` Rule: anything produced at runtime must land in surface B. **A feature that disappears when the database is recreated is not delivered.** If user asks "how to add a new module/screen", AI must answer with this exact operational sequence. ### 31.1 Step 1 - Create ListForm definition Define a new `ListForm` with at least: - `ListFormCode`, `Name`, `Title` - `DataSourceCode` - `SelectCommandType` + `SelectCommand` - `KeyFieldName` + `KeyFieldDbSourceType` - `PermissionJson` (create/read/update/delete/export/import/note) - `EditingOptionJson` + `EditingFormJson` - `FilterRowJson`, `HeaderFilterJson`, `SearchPanelJson`, `GroupPanelJson` - `SelectionJson`, `ColumnOptionJson`, `PagerOptionJson` - `InsertServiceAddress`, `UpdateServiceAddress`, `DeleteCommand` For parent-child scenarios, define `SubFormsJson` relation mapping (ParentFieldName -> ChildFieldName, DbType). ### 31.2 Step 2 - Create ListFormFields For each field, define runtime behavior through `ListFormField` records: - Data binding: field name, db type, source - UI behavior: visibility, order, width, grouping, fixed/band settings - Editing behavior: editor type (`dxTextBox`, `dxSelectBox`, `dxNumberBox`, etc.), required, options - Lookup behavior: data source type, display/value members, cascade rules - Validation rules and default values - Command/action columns where needed AI must prioritize metadata-based field behavior instead of suggesting custom React forms. ### 31.3 Step 3 - Add menu and route mapping From `MenusData.json` patterns, AI must define both: - `Routes` entry: - `key`, `path`, `componentPath`, `routeType`, `authority` - `Menus` entry: - `ParentCode`, `Code`, `DisplayName`, `Url`, `Icon`, `RequiredPermissionName`, `Order` For dynamic list screens, base route pattern is: - `/admin/list/{ListFormCode}` AI must ensure menu URL and route path point to same screen contract. ### 31.4 Step 4 - Add permissions From `PermissionsData.json` patterns, AI must define: - Permission group (if missing) - Permission definitions for feature root and actions - Menu permission binding (`RequiredPermissionName`) Minimum action set suggestion: - `.Default` - `.Create` - `.Update` - `.Delete` - `.Export` - `.Import` - `.Note` AI must never propose menu/route without permission contract. ### 31.5 Step 5 - Add settings/integration dependencies From `HostData.json` patterns, AI must define required settings when feature depends on external services: - Sender credentials and endpoints (sms/mail/whatsapp/rocket) - AiBot integration endpoint and activation state - Feature-specific setting keys, providers, encryption flags If integration required and setting missing, AI must explicitly add "blocking prerequisite" note. ### 31.6 Step 6 - Data and service contract AI must produce one of: - Direct table/view select command - Parameterized SQL via managed query - Custom endpoint / dynamic service AI must include tenant-safe filters and avoid string concatenation. ### 31.7 Step 7 - Delivery checklist For every new low-code feature, AI output must list: 1. New `ListForm` code and purpose 2. `ListFormField` set (critical columns/editors/lookups) 3. Route record 4. Menu record 5. Permission records 6. Required settings/integrations 7. Validation and rollback notes If this 7-item list is incomplete, proposal is not accepted. ### 31.8 AI response style for implementation requests When user asks for a screen/module, AI must answer in this order: 1. Decision flow class (A/B/C/D) 2. Seeder-style artifacts to add (ListForm, Fields, Menu, Route, Permission, Setting) 3. If needed, SQL/endpoint contract 4. Test and rollback checklist AI should produce practical, copy-adaptable artifact definitions and avoid abstract-only explanations. ### 31.9 Screen brief, discovery questions and image-based requests A request arrives either as prose or as a **screenshot / mockup / photo** of the desired screen. Both enter the same pipeline — and for a **new screen/component the pipeline starts with questions, not files**: 1. **Run the discovery round** (`lowcode.instructions.md` §0.6). Ask in batches (max 4 questions per round, each with 2–4 options and a recommended default), max three rounds. Round 1 always covers: screen kind (List/Wizard vs Custom Component vs single-record form), data source, owning module, and tenancy scope. Round 2 uses the path-specific question set (§0.6.3 for List, §0.6.4 for Custom). Never ask what the repo already answers — search first, confirm findings instead of re-asking. If the user says "you decide / don't ask", skip the round and build on defaults. 2. Turn the answers into a single screen brief — purpose, data source, columns and editors, layouts, actions, sub-screens, widgets, approval flow, menu, permissions, tenancy, language texts. For an image, read it first (page regions → main region type → toolbar → columns → filters → form controls → row actions); the image answers most of round 2, so ask only what an image cannot show (§0.7.4). 3. **Get the brief confirmed before writing any seed file.** Mark defaulted lines `(varsayılan)` so the user sees what they are approving. 4. Pick the tool: grid-shaped work → SQL Query Manager + Wizard; free layout / dashboard / single-record custom form → Custom Component. **When in doubt, recommend the Wizard.** 5. Produce the seed files in dependency order, then list them and restate the defaulted choices. An image is a reference, not a pixel contract: build the closest thing the platform's own components give, and state at the end what had no equivalent. Three things are never assumed, always asked: which table is written to, who approves (when there is an approval flow), and the formula of any calculation/business rule. This question-first rule applies only to producing a **new** screen/component; fixes, field additions, debugging and explanations proceed directly. The concrete question sets, brief template, the image-element → artifact mapping and the visual-control → `EditorType` mapping are in `lowcode.instructions.md` §0.6–§0.7. --- ## 32. Mandatory Default Behaviors (Do Not Ask Repeatedly) The following defaults are mandatory unless user explicitly overrides them. ### 32.1 SQL Table Designer default column behavior When user requests creating a table and only specifies business columns (for example `FullName`, `Phone`), AI must still ensure platform-standard technical columns are included by default in SQL Query Manager table design flow: - `TenantId` (multi-tenant compatibility) - Full audited set: - `Id` - `CreationTime` - `CreatorId` - `LastModificationTime` - `LastModifierId` - `IsDeleted` - `DeletionTime` - `DeleterId` Rules: - Do not require user to explicitly request these columns each time. - If user explicitly says "no tenant" or "no audit", then respect that override. - Primary key strategy must remain compatible with existing index/key policy. ### 32.2 Wizard menu parent fallback behavior When creating menu via Wizard/ListForm and user does not explicitly specify parent menu: - Do not auto-place under `Definitions` by default. - Create (or use) a dedicated new top-level parent menu for that feature/module. - Assign top-level menu `Order` as next available order (`max(Order) + 1`) among root menus. - Place the generated list/menu item under this newly created parent. Rules: - `Definitions` can be used only when user explicitly selects it. - Permission contract must be created/bound for both parent and child menu items. - Route/menu consistency remains mandatory (`Url` and screen contract must match). ### 32.3 Wizard component kind and menu creation `ListFormWizardDto.ComponentKind` is the wizard's fork and the first field in the seed file. Default is `List`, so older seed files that lack it keep working. | Kind | Produces | Menu URL | Child permissions | | --- | --- | --- | --- | | `List` | ListForm + ListFormFields + Route + Menu + Permissions + language keys | `/admin/list/{ListFormCode}` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` | | `Custom` | Menu + Permissions + language keys; the screen is the selected Custom Component | the component's `RoutePath` | `.Create`, `.Update`, `.Delete` only | `Export` / `Import` / `Note` exist only in the ListForm ecosystem. Never propose them on the `Custom` path. `CreateMenu = false` (menu-less wizard): no menu record — and no parent menu record — is created; ListForm, permissions and language keys are still produced. Use it when the screen is meant to be embedded elsewhere as a SubGrid/part. Default is `true`. ### 32.4 Permission group selection The wizard's permission group comes from a select list backed by `GetWizardPermissionGroups`. Display names are read from the language key whose name equals the group name. - Selecting an existing group fills `PermissionGroupEn/Tr` (seed file: `Language.PermissionGroupEn/Tr`); if left blank the server writes the database values back, so the seed file always carries both languages. - Defining a new group writes those texts to the language key with the same name as the group. - AI must never propose a permission group without EN + TR display names. ### 32.5 Wizard export/import is a package, not a file Exporting a wizard produces a zip whose paths are relative to the scope folder (`host` / `tenants/{tenantId}`) and which carries every dependency: The closure is **recursive** (`WizardExportCollector`): a sub-form's wizard file is queued and its own dependencies are collected under the same rules, so the archive stands on its own. - `wizard/{file}.json` — the screen itself plus the wizard files of the sub-forms it references through `SubFormsDto[].Code`, recursively - `data/App.DeveloperKit.CustomComponents.json` — `Custom` path; the file is filtered down to the exported component and its dependency chain, never exported whole - `data/{ListFormCode}.json` — the screen's own data mirror when `SeedFilePath` is set - `crud/{entity}.json` — endpoints those components use - `{sql|postgres}/{object|execute}/{object}.sql` — the object behind `SelectCommand`, the objects a lookup query (`LookupDto.LookupQuery`) references through `FROM`/`JOIN`, and on the `Custom` path the tables the CRUD endpoints sit on Script files are never guessed from a fixed provider/folder combination: the scope's script folders are scanned from disk, and every file matching the object name is added. A missing script is **not** a warning — not every table is seed-managed (ABP's own tables come from EF migrations); whether the target has it is decided by the import analysis against the real database, never assumed here. An unresolved **recorded reference** (a sub-form, a `SeedFilePath` mirror, a `DataSources[].SeedFile`, a component row named in a seed file but with no counterpart) does not block the download but is never silent: `ExportAnalysis` returns the full archive contents plus what is missing, and the File Manager shows that list before downloading. The import screen is a four-step wizard (`Steps`): **Files** (resolve conflicts) → **Checks** (findings; a schema finding opens the existing `SqlTableDesignerDialog`, and the analysis re-runs after a deploy) → **Plan** (the full list of what confirming will do, in the server's completion order) → **Apply**. Going back is always allowed; going forward is gated — unresolved conflicts block Checks, a blocking finding blocks Plan. Server-side it is two phases: `AnalyzeImport` (unzip to `.imports/{id}/staged`, classify each entry as `New` / `Identical` / `Conflict`) then `ApplyImport` (file-by-file write, previous content backed up so `RollbackImport` can undo the whole batch), closed by `CompleteImport`. `AnalyzeImport` also **validates** before anything is written (`WizardImportValidator`). With at least one `Error` finding the session is marked `blocked` on the server and `ApplyImport` refuses, so the check cannot be bypassed by calling the endpoint directly. Errors: malformed JSON; a wizard file with no `Wizard`/`ListForm` section; a screen's or a `crud/{Entity}.json`'s table missing in the target **and** absent from the archive's scripts; a `data/{code}.json` whose ListForm exists neither in the archive nor in the target; an unresolvable custom-component `Dependencies` / `DataSources[].SeedFile`; an empty component name or route on the `Custom` path; a sub-form reference with no counterpart. Warnings: object existence could not be queried, provider mismatch (`sql` ↔ `postgres`), a missing `SeedFilePath` data file, an empty file. The analysis also **compares the table schema**. Generated table scripts are wrapped in `IF OBJECT_ID(...) IS NULL BEGIN CREATE TABLE ... END`, so when the target already has the table the `CREATE` block is skipped entirely and a new column never arrives from the script itself — the screen would then fail with an invalid-column error. The incoming `CREATE TABLE` declaration (`SqlCreateTableParser`) is compared against the live `INFORMATION_SCHEMA.COLUMNS` shape. The split follows the risk of loss: **a missing column is added** — adding is lossless, so it is reported as a Warning with the statement it will run (`WizardImportIssueDto.Statement`) and applied at completion; the one exception is a `NOT NULL` column with no default on a non-empty table, which the database itself would reject and where the value to write is a business decision — that blocks. **An existing column is never changed**: a length / precision / scale / nullability difference is an Error and stops the import, because narrowing truncates and a type conversion can fail. An extra column in the target is a Warning and is never dropped; no path emits `DROP TABLE`, `DROP COLUMN` or `ALTER COLUMN`. The comparison lives in one place (`SqlTableSchemaComparer`) and both the analysis and the completion use it. Type **names** are deliberately not compared: the provider catalog reports its own canonical name (`VARCHAR` → `character varying` on PostgreSQL), so a textual comparison would raise a false difference on every `VARCHAR` column. A `data/*.json` entry is compared and merged **by row, not by file**: the diff shows only the rows whose keys the archive carries (file-level `GeneratedAt`/`Order` are excluded), so unrelated records never surface as a difference — no matching row means `New` (insert), a matching identical row means `Identical` (nothing is transferred), a matching different row means `Conflict` (update). The file itself is never overwritten: rows are merged into the target by key (incoming row wins, target-only rows are kept). `CompleteImport` then applies exactly the `data/` files it wrote to the database through `IListFormSeedDataApplier` with `overwriteExisting: true` — a missing row is inserted, an existing one is updated from the file (unlike migrate/seed, where a possibly stale file must not overwrite live data), and a row that was soft deleted is revived by clearing the columns the screen's `DeleteCommand` sets (`IsDeleted`/`DeletionTime`/`DeleterId`), otherwise the default filter would keep hiding it — and reports the inserted/updated/skipped counts. Data files that were already identical to the target are applied too: there was nothing to write, but the rows may be missing or soft deleted in the database. A file the user chose to keep (different, not written) is left alone. This step is not covered by `RollbackImport`, which only restores files and must be run before it. `CompleteImport` runs in **dependency order and the order is fixed**: (a) the `.sql` scripts under `{sql|postgres}/{object,execute}` (`object` first) — a missing table is created here, otherwise the later steps would fail on it; (b) columns missing from existing tables are added; (c) `crud/*.json` through `ICrudEndpointSeedApplier`; (d) the `data/*.json` rows; (e) `wizard/*.json` — the screens are installed through the same path the wizard screen uses (`DeployWizardAsync`), reusing the file's `ListForm`/`Fields` contract as-is instead of rebuilding it from the wizard answers. A screen that already exists is left untouched, and each screen is installed in its own unit of work. No migrate run is needed: the screen works as soon as the import finishes. Limits: allowed root folders `wizard`, `crud`, `data`, `sql`, `postgres` (and only `object` / `execute` under the SQL provider folders); 5 MB per entry, 50 MB per archive, 500 entries. Permissions: `App.Listforms.Wizard.Export` / `.Import`. AI must not propose moving a screen between environments by copying a single json file. ### 32.6 AI enforcement requirement AI must proactively apply these defaults in: - implementation proposals, - code/seed changes, - migration and wizard behavior recommendations. If AI output omits these defaults without explicit user override, output is non-compliant.