Two files, one repository
delphicleancode/delphi-spec-kit ships 3 formats across 3 indexed files. The question worth asking is whether the second one says anything the first does not.
| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 55 | 4 | 0% |
| Commands | 0 | 0 | 0 | — |
| Section tags | 4 | 4 | 0 | 50% |
What each file covers
Sections
0 shared · 55 only in A · 4 only in B- − Delphi AI Spec-Kit — AGENTS.md
- − Language and Stack
- − Naming Conventions — Pascal Guide
- − General Rule
- − Mandatory Prefixes
- − Unit Naming
- − Method Naming
- − Unit Test Naming (TDD)
- − Naming of Forms and DataModules
- − Components in Forms
- − DevExpress (DEXT) components in Forms
- − ACBr Project (Commercial Automation)
- − Intraweb Components (Web)
- − REST Frameworks (Horse, DMVC, Dext)
- − Dext Framework
- − Horse Framework
- − DelphiMVCFramework (DMVC)
- − DevExpress Components
- − Firebird Database
- − Mandatory Connection Configuration
- − Essential Rules Firebird
- − Firebird Anti-Patterns
- − PostgreSQL Database
- − Connection Configuration
- − Essential Rules PostgreSQL
- − PostgreSQL Anti-Patterns
- − MySQL / MariaDB Database
- − Essential Rules MySQL
- − MySQL Anti-Patterns
- − Threads and Multi-Threading
- − Golden Rule
- − Threading Approaches
- − Thread-Safety
- − Threading Anti-Patterns
- − SOLID principles in Delphi
- − S — Single Responsibility Principle (SRP)
- − O — Open/Closed Principle (OCP)
- − L — Liskov Substitution Principle (LSP)
- − I — Interface Segregation Principle (ISP)
- − D — Dependency Inversion Principle (DIP)
- − Clean Code — Essential Rules
- − 1. Short Methods
- − 2. Self-Descriptive Names
- − 3. Avoid Magic Numbers
- − 4. Guard Clauses
- − 5. Focused and Typed Try/Except
- − 6. Unit Organization
- − Recommended Design Patterns
- − Anti-Patterns to Avoid
- − Memory Management (Critical)
- − Documentation
- − Layer Structure (Architecture)
- − 🚫 AI Context Policy — What to Include and Exclude
- − Files AI Must Always Use as Context
- − Files AI Must Never Use as Context
- + Delphi AI Spec-Kit
- + Project Stack
- + Crucial Directives (Memory Management)
- + File Organization & Naming (PascalCase)
Commands
neither file has anySection tags
4 shared · 4 only in A · 0 only in B- − architecture
- − testing-strategy
- − database
- − docs
- test
- code-style
- performance
- do-not
Line diff
delphicleancode/delphi-spec-kit · AGENTS.md
@@ −1 @@
1# Delphi AI Spec-Kit — AGENTS.md
2
3> This file is automatically recognized by **Antigravity**, **GitHub Copilot**, **Cursor** and **Kiro**.
4> It defines the universal rules for Delphi development with AI.
5
6## Language and Stack
7
8- **Language:** Object Pascal (Delphi)
9- **Native IDE:** RAD Studio / Delphi
10- **Frameworks:** VCL, FMX, FireDAC
11- **Database:** FireDAC (SQLite, PostgreSQL, Firebird, SQL Server)
12- **Tests:** DUnitX
13- **Build:** MSBuild / Delphi Compiler (dcc32/dcc64)
14- **File extensions:** `.pas` (units), `.dfm`/`.fmx` (forms), `.dpr` (project), `.dpk` (package), `.dproj` (project config)
15
16## Naming Conventions — Pascal Guide
17
18### General Rule
19
20Use **PascalCase** (InfixCaps) for all identifiers. Reserved words are always in **TOK_6__, `end`, `if`, `then`, `else`, `nil`, `string`).
21
22### Mandatory Prefixes
23
24| Type | Prefix | Example |
25|------|---------|---------|
26| Class | `T` | `TCustomerRepository` |
27| Interface | `I` | `ICustomerRepository` |
28| Exception | `E` | `ECustomerNotFound` |
29| Private field | `F` | `FCustomerName` |
30| Parameter | `A` | `ACustomerName` |
31| Enumerated type | `T` | `TOrderStatus` |
32| Enum Items | short prefix | `osNew`, `osPending`, `osClosed` |
33
34### Unit Naming
35
36```
37NomeProjeto.Camada.Dominio.Funcionalidade.pas
38```
39
40Examples:
41
42- `MyApp.Domain.Customer.Entity.pas`
43- `MyApp.Infra.Customer.Repository.pas`
44- `MyApp.Application.Customer.Service.pas`
45- `MyApp.Presentation.Customer.View.pas`
46
47### Method Naming
48
49- Action methods: use verbs — `Execute`, `CreateOrder`, `ValidateCustomer`
50- Getters: prefix `Get` — `GetCustomerName`
51- Setters: prefix `Set` — `SetCustomerName`
52- Boolean functions: prefix `Is`, `Has`, `Can` — `IsValid`, `HasPermission`, `CanDelete`
53
54### Unit Test Naming (TDD)
55
56- Follow the generic behavioral pattern in DUnitX tests: `Action_Condition_ExpectedResult`
57- Example: `ProcessOrder_WithoutStock_RaisesException`, `CalculateTotal_WithDiscount_ReturnsLowerValue`
58- Create fakes in the test unit with prefix `TFake` (ex: `TFakeInventoryRepository`)
59
60### Naming of Forms and DataModules
61
62- Type: `TfrmCustomerEdit`, `TdmDatabase`
63- Variable: `frmCustomerEdit`, `dmDatabase`
64- Unit: `MyApp.Presentation.Customer.Edit.pas`
65
66### Components in Forms
67
68Use a 3-letter prefix indicating the type:
69
70| Component | Prefix | Example |
71|-----------|---------|---------|
72| TButton | `btn` | `btnSave` |
73| TEdit | `edt` | `edtName` |
74| TLabel | `lbl` | `lblName` |
75| TComboBox | `cmb` | `cmbStatus` |
76| TDBGrid | `dbg` | `dbgCustomers` |
77| TPanel | `pnl` | `pnlTop` |
78| TPageControl | `pgc` | `pgcMain` |
79| TTabSheet | `tab` | `tabSearch` |
80| TDataSource | `ds` | `dsCustomers` |
81| TFDQuery | `qry` | `qryCustomers` |
82| TFDConnection | `con` | `conMain` |
83| TMemo | `mmo` | `mmoObservation` |
84| TCheckBox | `chk` | `chkActive` |
85| TDateTimePicker | `dtp` | `dtpBirthDate` |
86| TImage | `img` | `imgPhoto` |
87| TListView | `lvw` | `lvwItems` |
88| TTreeView | `tvw` | `tvwCategories` |
89| TToolBar | `tlb` | `tlbMain` |
90| TActionList | `act` | `actMain` |
91| TPopupMenu | `pmn` | `pmnGrid` |
92| T Hours | `tmr` | `tmrRefresh` |
93| TStatusBar | `stb` | `stbMain` |
94
95### DevExpress (DEXT) components in Forms
96
97| Component | Prefix | Example |
98|-----------|---------|---------|
99| TcxGrid | `grd` | `grdCustomers` |
100| TcxGridDBTableView | `tvw` | `tvwCustomers` |
101| TcxDBTreeList | `trl` | `trlCategories` |
102| TdxLayoutControl | `lyt` | `lytMain` |
103| TdxLayoutGroup | `lgrp` | `lgrpPersonal` |
104| TdxLayoutItem | `litm` | `litmName` |
105| TcxDBTextEdit | `edt` | `edtName` |
106| TcxDBComboBox | `cmb` | `cmbStatus` |
107| TcxDBDateEdit | `dte` | `dtpBirthDate` |
108| TcxDBCurrencyEdit | `cur` | `curPrice` |
109| TcxDBLookupComboBox | `lcb` | `lcbCity` |
110| TdxBarManager | `bar` | `barMain` |
111| TdxRibbon | `rbn` | `rbnMain` |
112| TdxSkinController | `skn` | `sknController` |
113
114### ACBr Project (Commercial Automation)
115
116| Component | Prefix | Example |
117|-----------|---------|---------|
118| TACBrNFe | `acbrNFe` | `acbrNFe1` ou `acbrNfeEmissor` |
119| TACBrCTe | `acbrCte` | `acbrCteMain` |
120| TACBrBoleto | `acbrBoleto` | `acbrBoletoCob` |
121| TACBrTEFD | `acbrTef` | `acbrTefVisa` |
122| TACBrPosPrinter | `acbrPosPrinter`| `acbrPosPrinterCaixa` |
123| TACBrSAT | `acbrSat` | `acbrSatFiscal` |
124| TACBrCEP | `acbrCep` | `acbrCepBusca` |
125
126**ACBr Note:** Avoid trapping the UI directly in component interactive events. Isolate tax logic.
127
128### Intraweb Components (Web)
129
130| Component | Prefix | Example |
131|-----------|---------|---------|
132| TIWAppForm | `iwForm`| `iwFormLogin` |
133| TIWButton | `iwBtn` | `iwBtnSave` |
134| TIWEdit | `iwEdt` | `iwEdtName` |
135| TIWLabel | `iwLbl` | `iwLblTitle` |
136| TIWComboBox | `iwCmb` | `iwCmbStatus` |
137| TIWGrid | `iwGrd` | `iwGrdItems` |
138| TIWRegion | `iwReg` | `iwRegContainer` |
139
140**Intraweb Note:** Avoid global unit variables to control user state. Always store transient data in `UserSession` to avoid leaks between sessions.
141
142## REST Frameworks (Horse, DMVC, Dext)
143
144### Dext Framework
145
146Dext (<https://github.com/cesarliws/dext>) is an enterprise landmark inspired by the .NET ecosystem. Conventions:
147
148- **Minimal APIs:** Use `App.Builder.MapGet` with Auto-Binding for DTOs
149- **Dependency Injection:** Mandatory. Inject into endpoints: `function(Dto: MyDto; Rep: ICustomerRepository): IResult`
150- **Entity ORM:** LINQ type queries (`DbContext.Where(U.Age > 18)`). Do not use queries in pure SQL chained.
151- **Async:** Use `TAsyncTask.Run` from `Dext.Core.Tasks`.
152- **Results:** Return typed frameworks or Records directly, serialized as JSON.
153
154### Horse Framework
155
156Horse is a minimalist REST framework for Delphi (Express style). Conventions:
157
158- **Controller:** Class with `class procedure RegisterRoutes`
159- **Handler:** `class procedure Nome(AReq: THorseRequest; ARes: THorseResponse; ANext: TProc)`
160- **Middleware:** `procedure Nome(AReq: THorseRequest; ARes: THorseResponse; ANext: TProc)`
161- **Routes:** Kebab-case, plural — `/api/customers`, `/api/order-items`
162- **JSON:** Use `Jhonson` middleware for automatic serialization
163- **CORS:** Use `Horse.CORS` middleware
164- **Structure:** Controllers separated from Services, Services separated from Repositories
165- **Packages:** `boss install horse horse-jhonson horse-cors horse-jwt`
166
167### DelphiMVCFramework (DMVC)
168
169DMVC is a classic MVC framework with Active Record, JWT and Swagger:
170
171- **Controller:** Inherits from `TMVCController` with `[MVCPath]` attribute
172- **Routes:** Attributes — `[MVCPath]`, `[MVCHTTPMethod]`, `[MVCProduces]`, `[MVCConsumes]`
173- **Active Record:** Inherits from `TMVCActiveRecord` with `[MVCTable]`, `[MVCTableField]`
174- **Serialization:** Automatic via `Render()` (JSON by default)
175- **WebModule:** `TMVCEngine` created in WebModule with controllers and middleware
176- **JWT:** `TMVCJWTAuthenticationMiddleware` built-in
177- **RQL:** Resource Query Language for filters via query string
178
179### DevExpress Components
180
181Advanced visual components for VCL:
182
183- **Grid:** `TcxGrid` with `TcxGridDBTableView` (data-aware)
184- **Layout:** `TdxLayoutControl` for responsive forms
185- **Skins:** `TdxSkinController` for global themes
186- **Export:** `cxGridExportLink` to Excel/PDF
187- **Filters:** `DataController.Filter` for programmatic filters
188
189## Firebird Database
190
191Firebird is the most used corporate database with Delphi. Access via **FireDAC** (driver `FB`).
192
193### Mandatory Connection Configuration
194
195```pascal
196FConnection.DriverName := 'FB';
197FConnection.Params.Values['CharacterSet'] := 'UTF8'; //ALWAYS UTF8
198FConnection.Params.Values['SQLDialect'] := '3'; //NEVER Dialect 1
199FConnection.Params.Values['Protocol'] := 'TCPIP'; //Or 'Local' for embedded
200FConnection.Params.Values['PageSize'] := '16384'; // 16KB recomendado
201FConnection.TxOptions.Isolation := xiReadCommitted; //Standard isolation
202```
203
204### Essential Rules Firebird
205
206- **Dialect 3 ALWAYS** — Dialect 1 is InterBase legacy and causes ambiguity with `DATE`
207- **CharacterSet UTF8** — required for correct accent support
208- **Parameterized queries** — never concatenate strings in SQL
209- **RETURNING with Open** — `INSERT ... RETURNING id` requires `LQuery.Open`, not `ExecSQL`
210- **Generators** for auto-increment with `BEFORE INSERT` triggers
211- **Domains** to centralize types and validations in the schema
212- **Stored Procedures:** Selectable (with `SUSPEND`) uses `SELECT FROM SP`; Executable uses `EXECUTE PROCEDURE`
213- **Explicit transactions** for compound operations (StartTransaction/Commit/Rollback)
214- **Treat deadlocks** via `EFDDBEngineException.Kind = ekRecordLocked`
215
216### Firebird Anti-Patterns
217
218- ❌ `SQLDialect := '1'` — ALWAYS use `'3'`
219- ❌ `ExecSQL` with `RETURNING` — use `Open`
220- ❌ Concatenate SQL — use parameters
221- ❌ Ignore `CharacterSet` — set `UTF8`
222- ❌ `PAGE_SIZE 4096` — use `16384` for production
223- ❌ Bypass deadlocks — handle `ekRecordLocked`
224- ❌ `CREATE TABLE IF NOT EXISTS` — does not exist in Firebird (check via `RDB$RELATIONS`)
225
226> **Skills:** `.gemini/skills/firebird-database/SKILL.md`
227> **Rules:** `.cursor/rules/firebird-patterns.md`
228
229## PostgreSQL Database
230
231PostgreSQL is the most advanced open-source database, ideal for modern projects. Access via **FireDAC** (driver `PG`).
232
233### Connection Configuration
234
235```pascal
236FConnection.DriverName := 'PG';
237FConnection.Params.Values['Server'] := 'localhost';
238FConnection.Params.Values['Port'] := '5432';
239FConnection.Params.Database := 'meubanco';
240FConnection.Params.UserName := 'postgres';
241FConnection.Params.Password := 'senha';
242FConnection.Params.Values['CharacterSet'] := 'UTF8';
243FConnection.TxOptions.Isolation := xiReadCommitted;
244```
245
246### Essential Rules PostgreSQL
247
248- **IDENTITY instead of SERIAL** — use `GENERATED ALWAYS AS IDENTITY` for new projects (PG 10+)
249- **RETURNING with Open** — `INSERT ... RETURNING id` requires `LQuery.Open`, not `ExecSQL`
250- **native UPSERT** — `INSERT ... ON CONFLICT (col) DO UPDATE SET ...`
251- **JSONB** — for semi-structured data, indexable with GIN
252- **ENUM types** — `CREATE TYPE status AS ENUM ('active', 'inactive')` mapped to Pascal enum
253- **PL/pgSQL** — Functions (`RETURNS TABLE` = Selectable), Procedures (`CALL`, PG 11+)
254- **Explicit transactions** — `StartTransaction/Commit/Rollback`, supports `SAVEPOINT`
255- **Full-Text Search** — `tsvector` + `tsquery` with GIN index
256- **Metadata via `information_schema`** — do not use `RDB$` (this is Firebird)
257
258### PostgreSQL Anti-Patterns
259
260- ❌ Concatenate SQL — use parameterized parameters
261- ❌ `ExecSQL` with `RETURNING` — use `Open`
262- ❌ `SERIAL` in new projects — use `IDENTITY`
263- ❌ `SELECT *` in large tables — select required columns
264- ❌ N+1 queries — use JOIN or subquery
265- ❌ Save JSON as TEXT — use `JSONB`
266- ❌ Ignore indexes on WHERE/JOIN columns
267
268> **Skills:** `.gemini/skills/postgresql-database/SKILL.md`
269> **Rules:** `.cursor/rules/postgresql-patterns.md`
270
271## MySQL / MariaDB Database
272
273MySQL is the most popular open-source database in the world. MariaDB is a compatible fork. Access via **FireDAC** (driver `MySQL`).
274
275### Connection Configuration
276
277```pascal
278FConnection.DriverName := 'MySQL';
279FConnection.Params.Values['Server'] := 'localhost';
280FConnection.Params.Values['Port'] := '3306';
281FConnection.Params.Database := 'meubanco';
282FConnection.Params.UserName := 'root';
283FConnection.Params.Password := 'senha';
284FConnection.Params.Values['CharacterSet'] := 'utf8mb4'; //NEVER 'utf8' (only 3 bytes!)
285FConnection.TxOptions.Isolation := xiReadCommitted;
286```
287
288### Essential Rules MySQL
289
290- **`utf8mb4` ALWAYS** — `utf8` in MySQL only has 3 bytes (does not support emoji). Use `utf8mb4`
291- **AUTO_INCREMENT + LAST_INSERT_ID()** — MySQL does NOT support `RETURNING`. Get ID via `LAST_INSERT_ID()`
292- **native UPSERT** — `INSERT ... ON DUPLICATE KEY UPDATE`
293- **Native JSON** — `JSON` type with `->>`/`JSON_EXTRACT` operators (MySQL 5.7+)
294- **InnoDB ALWAYS** — never MyISAM in new projects (needs FK and transactions)
295- **Stored Procedures** — `CALL sp_nome(...)` for procedures, `SELECT fn_nome(...)` for functions
296- **Explicit transactions** — `StartTransaction/Commit/Rollback`, supports `SAVEPOINT`
297- **COLLATE** — `utf8mb4_unicode_ci` for correct case-insensitive comparison
298- **Metadata via `information_schema`** — use `DATABASE()` for current schema
299
300### MySQL Anti-Patterns
301
302- ❌ `utf8` as charset — use `utf8mb4`
303- ❌ Try `RETURNING` — does not exist, use `LAST_INSERT_ID()`
304- ❌ `MyISAM` in new tables — use `InnoDB`
305- ❌ Concatenate SQL — use parameters
306- ❌ `SELECT *` without `LIMIT` — page results
307- ❌ N+1 queries — use JOIN or subquery
308- ❌ Ignore indexes on WHERE/JOIN columns
309
310> **Skills:** `.gemini/skills/mysql-database/SKILL.md`
311> **Rules:** `.cursor/rules/mysql-patterns.md`
312
313## Threads and Multi-Threading
314
315Threads are essential for keeping the UI responsive and processing data in parallel. Delphi offers `TThread`, PPL (`TTask`, `TParallel.For`, `TFuture<T>`) and synchronization primitives.
316
317### Golden Rule
318
319> **NEVER access visual components (VCL/FMX) directly from a secondary thread.**
320> Use `TThread.Synchronize` (blocking) or `TThread.Queue` (non-blocking) to update the UI.
321
322### Threading Approaches
323
324| Approach | When to Use |
325|-----------|-------------|
326| `TThread.CreateAnonymousThread` | Simple, one-shot tasks |
327| `TTask.Run` (PPL) | Modern way, managed pool |
328| `TParallel.For` | Parallel loop in independent collections |
329| `TFuture<T>` | Asynchronous result with return value |
330| `TThread` (inheritance) | Permanent workers, queues, servers |
331
332### Thread-Safety
333
334- **`TCriticalSection`** — Classic critical section (`Enter`/`Leave` ALWAYS in `finally`)
335- **`TMonitor`** — Native object lock (`Enter`/`Exit`)
336- **`TInterlocked`** — Atomic operations (`Increment`, `Decrement`, `Exchange`)
337- **`TThreadList<T>`** — Thread-safe list with `LockList`/`UnlockList`
338- **`TMultiReadExclusiveWriteSynchronizer`** — Cache: multiple reads, few writes
339- **`TThreadedQueue<T>`** — Thread-safe queue for Producer-Consumer
340
341### Threading Anti-Patterns
342
343- ❌ Access VCL/FMX directly from secondary thread
344- ❌ `Sleep()` in the main thread (freezes the UI!)
345- ❌ `FreeOnTerminate := True` + `WaitFor` (crash!)
346- ❌ Access shared variables without locking
347- ❌ Ignore exceptions in threads (they are silent!)
348- ❌ `TCriticalSection.Leave` fora de `finally`
349
350> **Skills:** `.gemini/skills/threading/SKILL.md`
351> **Rules:** `.cursor/rules/threading-patterns.md`
352
353## SOLID principles in Delphi
354
355### S — Single Responsibility Principle (SRP)
356
357Each unit and each class must have **a single responsibility**:
358
359```pascal
360//✅ GOOD — separate responsibilities
361TCustomerValidator = class
362 function Validate(ACustomer: TCustomer): TValidationResult;
363end;
364
365TCustomerRepository = class(TInterfacedObject, ICustomerRepository)
366 function FindById(AId: Integer): TCustomer;
367 procedure Save(ACustomer: TCustomer);
368end;
369
370//❌ BAD — class doing it all
371TCustomer = class
372 procedure Validate; //should be a Validator
373 procedure SaveToDb; //should be a Repository
374 procedure SendEmail; //should be a Service
375end;
376```
377
378### O — Open/Closed Principle (OCP)
379
380Classes should be **open for extension**, closed for modification. Use inheritance and interfaces:
381
382```pascal
383type
384 IReportExporter = interface
385 procedure Export(AReport: TReport);
386 end;
387
388 TPdfExporter = class(TInterfacedObject, IReportExporter)
389 procedure Export(AReport: TReport);
390 end;
391
392 TExcelExporter = class(TInterfacedObject, IReportExporter)
393 procedure Export(AReport: TReport);
394 end;
395```
396
397### L — Liskov Substitution Principle (LSP)
398
399Subtypes must be replaceable with the base type without breaking behavior:
400
401```pascal
402//✅ GOOD — any ICustomerRepository works
403procedure TCustomerService.LoadCustomer(ARepo: ICustomerRepository);
404begin
405 //works with TFireDACCustomerRepo, TMemoryCustomerRepo, TMockCustomerRepo
406 FCustomer := ARepo.FindById(FCustomerId);
407end;
408```
409
410### I — Interface Segregation Principle (ISP)
411
412Small, cohesive interfaces, not "fat" interfaces:
413
414```pascal
415//✅ GOOD — segregated interfaces
416type
417 IReadableRepository<T> = interface
418 function FindById(AId: Integer): T;
419 function FindAll: TObjectList<T>;
420 end;
421
422 IWritableRepository<T> = interface
423 procedure Save(AEntity: T);
424 procedure Delete(AId: Integer);
425 end;
426
427 ICustomerRepository = interface(IReadableRepository<TCustomer>)
428 ['{GUID}']
429 function FindByCpf(const ACpf: string): TCustomer;
430 end;
431```
432
433### D — Dependency Inversion Principle (DIP)
434
435Depend on **abstractions** (interfaces), not concrete implementations. Use **constructor injection**:
436
437```pascal
438type
439 TOrderService = class
440 private
441 FOrderRepo: IOrderRepository;
442 FNotifier: INotificationService;
443 public
444 constructor Create(AOrderRepo: IOrderRepository; ANotifier: INotificationService);
445 procedure PlaceOrder(AOrder: TOrder);
446 end;
447
448constructor TOrderService.Create(AOrderRepo: IOrderRepository; ANotifier: INotificationService);
449begin
450 inherited Create;
451 FOrderRepo := AOrderRepo;
452 FNotifier := ANotifier;
453end;
454```
455
456## Clean Code — Essential Rules
457
458### 1. Short Methods
459
460- Maximum **20 lines** per method (ideal: 5-10)
461- If a method needs a comment explaining "what it does", it should be extracted into a method with a descriptive name
462
463### 2. Self-Descriptive Names
464
465```pascal
466//❌ SPACIOUS
467procedure Proc1(S: string; N: Integer);
468function Calc(V: Double): Double;
469
470// ✅ BOM
471procedure SendNotificationEmail(const ARecipientEmail: string; ATemplateId: Integer);
472function CalculateDiscountedPrice(AOriginalPrice: Double): Double;
473```
474
475### 3. Avoid Magic Numbers
476
477```pascal
478//❌ SPACIOUS
479if ACustomer.Age > 18 then
480
481// ✅ BOM
482const
483 MINIMUM_AGE = 18;
484// ...
485if ACustomer.Age > MINIMUM_AGE then
486```
487
488### 4. Guard Clauses
489
490```pascal
491//❌ BAD — excessive nesting
492procedure ProcessOrder(AOrder: TOrder);
493begin
494 if Assigned(AOrder) then
495 begin
496 if AOrder.Items.Count > 0 then
497 begin
498 if AOrder.IsValid then
499 begin
500 //real logic here
501 end;
502 end;
503 end;
504end;
505
506//✅ BOM — guard clauses
507procedure ProcessOrder(AOrder: TOrder);
508begin
509 if not Assigned(AOrder) then
510 raise EArgumentNilException.Create('AOrder cannot be nil');
511 if AOrder.Items.Count = 0 then
512 raise EBusinessRuleException.Create('Order must have at least one item');
513 if not AOrder.IsValid then
514 raise EValidationException.Create('Order validation failed');
515
516 //real logic here — no nesting
517end;
518```
519
520### 5. Focused and Typed Try/Except
521
522```pascal
523//❌ BAD — generic catch swallowing critical errors (Access Violation, OOM)
524try
525 //large block of long code
526except
527 on E: Exception do //Or worse: without declaring "on E:"
528 ShowMessage(E.Message);
529end;
530
531//✅ GOOD — specific exceptions and granular recovery
532try
533 FConnection.Open;
534 PerformCriticalAction;
535except
536 on E: EFDDBEngineException do
537 raise EDatabaseConnectionException.Create('Falha local no banco: ' + E.Message);
538 on E: EBusinessRuleException do
539 raise; //Pass the exception to the Controller to catch
540 on E: Exception do
541 begin
542 Logger.LogError('Critical unexpected failure', E);
543 raise; //NEVER hide pure root Exception exceptions without rethrowing!
544 end;
545end;
546```
547
548### 6. Unit Organization
549
550```pascal
551unit MyApp.Domain.Customer.Entity;
552
553interface
554
555uses
556 System.SysUtils,
557 System.Classes,
558 System.Generics.Collections;
559
560type
561 //1. Types, enums and records first
562 TCustomerStatus = (csActive, csInactive, csSuspended);
563
564 //2. Interfaces
565 ICustomer = interface
566 ['{GUID}']
567 function GetName: string;
568 property Name: string read GetName;
569 end;
570
571 //3. Classes
572 TCustomer = class(TInterfacedObject, ICustomer)
573 private
574 FId: Integer;
575 FName: string;
576 FStatus: TCustomerStatus;
577 function GetName: string;
578 public
579 //Constructor and Destructor first
580 constructor Create(const AName: string);
581 destructor Destroy; override;
582
583 //After public methods
584 function IsActive: Boolean;
585 procedure Activate;
586 procedure Deactivate;
587
588 //Properties last
589 property Id: Integer read FId write FId;
590 property Name: string read GetName;
591 property Status: TCustomerStatus read FStatus;
592 end;
593
594implementation
595
596{ TCustomer }
597
598constructor TCustomer.Create(const AName: string);
599begin
600 inherited Create;
601 if AName.Trim.IsEmpty then
602 raise EArgumentException.Create('Customer name cannot be empty');
603 FName := AName.Trim;
604 FStatus := csActive;
605end;
606
607//... other implementations
608```
609
610## Recommended Design Patterns
611
612| Standard | Use in Delphi |
613|--------|---------------|
614| **Repository** | Abstracts data access via interface (FireDAC, REST, etc.) |
615| **Service** | Contains business logic orchestrating repositories and other services |
616| **Factory** | Creates instances of complex objects or with dependencies |
617| **Observer** | Use `TNotifyEvent` or interfaces to decouple notifications |
618| **Strategy** | Interfaces to vary algorithms (e.g. tax calculation) |
619| **Unit of Work** | Manages database transactions |
620
621## Anti-Patterns to Avoid
622
623- ❌ **God class / God unit** — units with thousands of lines doing everything
624- ❌ **Direct coupling to forms** — business logic in `OnClick` of buttons
625- ❌ **Uses circular** — resolved by separating into layers (Domain, Infra, Application, Presentation)
626- ❌ **Global variables** — use dependency injection
627- ❌ **Hardcoded Strings** — use `resourcestring` or constants
628- ❌ **Ignoring memory management** — always free unmanaged objects by reference
629- ❌ **`with` statement** — avoid `with` as it reduces readability and makes debugging difficult
630- ❌ **Testing in Banco Real** — attach DUnitX projects directly to `TFDConnection`, skipping Mocks/Fakes.
631
632## Memory Management (Critical)
633
634- **Watched Blocks:** The golden rule in Delphi: Whenever there is code calling `.Create` for instances of TObject Classes, the IMMEDIATELY subsequent line must be a `try`. NO intermediate lines of code!
635
636```pascal
637//✅ The Gold Standard for Disposable Objects
638var LList: TStringList;
639begin
640 LList := TStringList.Create;
641 try
642 LList.Add('item');
643 // ...
644 finally
645 LList.Free; //i FreeAndNil(LList)
646 end;
647end;
648
649//✅ Objects with owner - VCL/FMX components
650TMyComponent := TMyComponent.Create(Self); //Owner (Self) assumes release
651
652//✅ Garbage Collection com Interfaces (ARC)
653//The object will be automatically cleaned up at the end of the scope, eliminating the need for try..free
654var LService: IMyService;
655begin
656 LService := TMyService.Create;
657 LService.DoSomething;
658end;
659
660//✅ Local variables: use L prefix
661var LCustomer: TCustomer;
662```
663
664## Documentation
665
666- Use **XMLDoc** for public methods and interfaces:
667
668```pascal
669///<summary>
670///Locates a customer by the CPF entered.
671///</summary>
672///<param name="ACpf">Customer CPF (numbers only)</param>
673///<returns>TCustomer instance or nil if not found</returns>
674///<exception cref="EArgumentException">If ACpf is empty</exception>
675function FindByCpf(const ACpf: string): TCustomer;
676```
677
678- Comments in **Portuguese** for Brazilian projects
679- Don't comment obvious code — let the method name explain
680
681## Layer Structure (Architecture)
682
683```
684src/
685├── Domain/ ← Entidades, Value Objects, Interfaces de repositório
686├── Application/ ← Services, Use Cases, DTOs
687├── Infrastructure/ ← Implementações de repositório (FireDAC), APIs externas
688└── Presentation/ ← Forms (VCL/FMX), ViewModels
689tests/
690└── Unit/ ← Projetos DUnitX e Fakes/Mocks isolados por contexto
691```
692
693> **Dependency rule:** `Presentation → Application → Domain ← Infrastructure`
694> The Domain **never** depends on other layers. `tests` depend on `Application` and `Domain` but inject Fake implementations by copying `Infrastructure` alone.
695
696---
697
698## 🚫 AI Context Policy — What to Include and Exclude
699
700> Full strategy documented in `docs/ai-ignore-strategy.md`.
701
702### Files AI Must Always Use as Context
703
704- `AGENTS.md` — universal rules
705- `README.md` — project overview
706- `.github/copilot-instructions.md` — Copilot pre-prompt
707- `.claude/CLAUDE.md` — Claude master prompt
708- `.claude/rules/**/*.md` — context-specific rules
709- `.claude/skills/**/SKILL.md` — on-demand skills
710- `.cursor/rules/**/*.md` — Cursor rules
711- `.gemini/skills/**/SKILL.md` — Gemini skills
712- `.kiro/steering/**/*.md` — Kiro steering docs
713- `examples/**/*.pas` — good practice examples
714- `docs/**/*.md` — documentation
715
716### Files AI Must Never Use as Context
717
718- Build artifacts: `*.dcu`, `*.exe`, `*.dll`, `*.bpl`, `*.dcp`, `*.map`, `*.res`
719- IDE temporaries: `*.local`, `*.identcache`, `*.stat`, `__history/`, `__recovery/`
720- Output directories: `Win32/`, `Win64/`, `Debug/`, `Release/`, `build/`, `dist/`
721- Secrets: `*.key`, `*.pfx`, `*.p12`, `.env`, `.env.*`
722- Noise: `*.log`, `*.dmp`, `*.bak`, `*.tmp`
723
724See `.cursorignore`, `.gitignore` and `.vscode/settings.json` for the enforced patterns.
725
726
delphicleancode/delphi-spec-kit · .claude/CLAUDE.md
@@ +1 @@
1# Delphi AI Spec-Kit
2
3This is the **Delphi AI Spec-Kit**, the master guide for Delphi (Object Pascal) development in this repository.
4
5## Project Stack
6- **Language:** Object Pascal (Delphi)
7- **Native IDE:** RAD Studio / Delphi
8- **Main Frameworks:** VCL, FMX, FireDAC
9- **Tests:** DUnitX
10- **Build / Tooling:** MSBuild, dcc32/dcc64, Boss (Package Manager)
11
12## Crucial Directives (Memory Management)
13- **Watched Blocks (Required):** EVERYTHING you instantiate with `.Create` (if it is `TObject` and does not have `Owner`) **MUST** have a `try..finally` on the IMMEDIATELY subsequent line.
14 ```pascal
15 Obj := TMyClass.Create;
16 try
17 Obj.DoSomething;
18 finally
19 Obj.Free; //my FreeAndNil(Obj)
20 end;
21 ```
22- **DO NOT use** `with`.
23- **DO NOT create** God Classes. Use SOLID Principles.
24- Isolate visual components (FMX/VCL) from strict business rules. Do not access DBGrid or form edits in pure logical units.
25- For dependency injection, pass abstractions in the constructor.
26
27## File Organization & Naming (PascalCase)
28- Classes: Start with `T` (ex: `TCustomer`).
29- Interfaces: Start with `I` (ex: `ICustomer`).
30- Exceptions: Start with `E` (ex: `EValidationError`).
31- Private attributes or fields: Start with `F` (ex: `FName`).
32- Local variables: Start with `L` (ex: `LCustomer`).
33- Parameters: Start with `A` (ex: `ACustomer`).
34- Unit nomenclature: `NomeProjeto.Camada.Dominio.Funcionalidade.pas`
35
36*(See the `AGENTS.md` global file and `rules/` folder for guidelines specific to frameworks such as FireDAC, Rest, Horse and Database).*
37
@@ −1 +1 @@
1−# Delphi AI Spec-Kit — AGENTS.md
1+# Delphi AI Spec-Kit
22
3−> This file is automatically recognized by **Antigravity**, **GitHub Copilot**, **Cursor** and **Kiro**.
4−> It defines the universal rules for Delphi development with AI.
3+This is the **Delphi AI Spec-Kit**, the master guide for Delphi (Object Pascal) development in this repository.
54
6−## Language and Stack
7−
5+## Project Stack
86 - **Language:** Object Pascal (Delphi)
97 - **Native IDE:** RAD Studio / Delphi
10−- **Frameworks:** VCL, FMX, FireDAC
11−- **Database:** FireDAC (SQLite, PostgreSQL, Firebird, SQL Server)
8+- **Main Frameworks:** VCL, FMX, FireDAC
129 - **Tests:** DUnitX
13−- **Build:** MSBuild / Delphi Compiler (dcc32/dcc64)
14−- **File extensions:** `.pas` (units), `.dfm`/`.fmx` (forms), `.dpr` (project), `.dpk` (package), `.dproj` (project config)
10+- **Build / Tooling:** MSBuild, dcc32/dcc64, Boss (Package Manager)
1511
16−## Naming Conventions — Pascal Guide
17−
18−### General Rule
19−
20−Use **PascalCase** (InfixCaps) for all identifiers. Reserved words are always in **TOK_6__, `end`, `if`, `then`, `else`, `nil`, `string`).
21−
22−### Mandatory Prefixes
23−
24−| Type | Prefix | Example |
25−|------|---------|---------|
26−| Class | `T` | `TCustomerRepository` |
27−| Interface | `I` | `ICustomerRepository` |
28−| Exception | `E` | `ECustomerNotFound` |
29−| Private field | `F` | `FCustomerName` |
30−| Parameter | `A` | `ACustomerName` |
31−| Enumerated type | `T` | `TOrderStatus` |
32−| Enum Items | short prefix | `osNew`, `osPending`, `osClosed` |
33−
34−### Unit Naming
35−
36−```
37−NomeProjeto.Camada.Dominio.Funcionalidade.pas
38−```
39−
40−Examples:
41−
42−- `MyApp.Domain.Customer.Entity.pas`
43−- `MyApp.Infra.Customer.Repository.pas`
44−- `MyApp.Application.Customer.Service.pas`
45−- `MyApp.Presentation.Customer.View.pas`
46−
47−### Method Naming
48−
49−- Action methods: use verbs — `Execute`, `CreateOrder`, `ValidateCustomer`
50−- Getters: prefix `Get` — `GetCustomerName`
51−- Setters: prefix `Set` — `SetCustomerName`
52−- Boolean functions: prefix `Is`, `Has`, `Can` — `IsValid`, `HasPermission`, `CanDelete`
53−
54−### Unit Test Naming (TDD)
55−
56−- Follow the generic behavioral pattern in DUnitX tests: `Action_Condition_ExpectedResult`
57−- Example: `ProcessOrder_WithoutStock_RaisesException`, `CalculateTotal_WithDiscount_ReturnsLowerValue`
58−- Create fakes in the test unit with prefix `TFake` (ex: `TFakeInventoryRepository`)
59−
60−### Naming of Forms and DataModules
61−
62−- Type: `TfrmCustomerEdit`, `TdmDatabase`
63−- Variable: `frmCustomerEdit`, `dmDatabase`
64−- Unit: `MyApp.Presentation.Customer.Edit.pas`
65−
66−### Components in Forms
67−
68−Use a 3-letter prefix indicating the type:
69−
70−| Component | Prefix | Example |
71−|-----------|---------|---------|
72−| TButton | `btn` | `btnSave` |
73−| TEdit | `edt` | `edtName` |
74−| TLabel | `lbl` | `lblName` |
75−| TComboBox | `cmb` | `cmbStatus` |
76−| TDBGrid | `dbg` | `dbgCustomers` |
77−| TPanel | `pnl` | `pnlTop` |
78−| TPageControl | `pgc` | `pgcMain` |
79−| TTabSheet | `tab` | `tabSearch` |
80−| TDataSource | `ds` | `dsCustomers` |
81−| TFDQuery | `qry` | `qryCustomers` |
82−| TFDConnection | `con` | `conMain` |
83−| TMemo | `mmo` | `mmoObservation` |
84−| TCheckBox | `chk` | `chkActive` |
85−| TDateTimePicker | `dtp` | `dtpBirthDate` |
86−| TImage | `img` | `imgPhoto` |
87−| TListView | `lvw` | `lvwItems` |
88−| TTreeView | `tvw` | `tvwCategories` |
89−| TToolBar | `tlb` | `tlbMain` |
90−| TActionList | `act` | `actMain` |
91−| TPopupMenu | `pmn` | `pmnGrid` |
92−| T Hours | `tmr` | `tmrRefresh` |
93−| TStatusBar | `stb` | `stbMain` |
94−
95−### DevExpress (DEXT) components in Forms
96−
97−| Component | Prefix | Example |
98−|-----------|---------|---------|
99−| TcxGrid | `grd` | `grdCustomers` |
100−| TcxGridDBTableView | `tvw` | `tvwCustomers` |
101−| TcxDBTreeList | `trl` | `trlCategories` |
102−| TdxLayoutControl | `lyt` | `lytMain` |
103−| TdxLayoutGroup | `lgrp` | `lgrpPersonal` |
104−| TdxLayoutItem | `litm` | `litmName` |
105−| TcxDBTextEdit | `edt` | `edtName` |
106−| TcxDBComboBox | `cmb` | `cmbStatus` |
107−| TcxDBDateEdit | `dte` | `dtpBirthDate` |
108−| TcxDBCurrencyEdit | `cur` | `curPrice` |
109−| TcxDBLookupComboBox | `lcb` | `lcbCity` |
110−| TdxBarManager | `bar` | `barMain` |
111−| TdxRibbon | `rbn` | `rbnMain` |
112−| TdxSkinController | `skn` | `sknController` |
113−
114−### ACBr Project (Commercial Automation)
115−
116−| Component | Prefix | Example |
117−|-----------|---------|---------|
118−| TACBrNFe | `acbrNFe` | `acbrNFe1` ou `acbrNfeEmissor` |
119−| TACBrCTe | `acbrCte` | `acbrCteMain` |
120−| TACBrBoleto | `acbrBoleto` | `acbrBoletoCob` |
121−| TACBrTEFD | `acbrTef` | `acbrTefVisa` |
122−| TACBrPosPrinter | `acbrPosPrinter`| `acbrPosPrinterCaixa` |
123−| TACBrSAT | `acbrSat` | `acbrSatFiscal` |
124−| TACBrCEP | `acbrCep` | `acbrCepBusca` |
125−
126−**ACBr Note:** Avoid trapping the UI directly in component interactive events. Isolate tax logic.
127−
128−### Intraweb Components (Web)
129−
130−| Component | Prefix | Example |
131−|-----------|---------|---------|
132−| TIWAppForm | `iwForm`| `iwFormLogin` |
133−| TIWButton | `iwBtn` | `iwBtnSave` |
134−| TIWEdit | `iwEdt` | `iwEdtName` |
135−| TIWLabel | `iwLbl` | `iwLblTitle` |
136−| TIWComboBox | `iwCmb` | `iwCmbStatus` |
137−| TIWGrid | `iwGrd` | `iwGrdItems` |
138−| TIWRegion | `iwReg` | `iwRegContainer` |
139−
140−**Intraweb Note:** Avoid global unit variables to control user state. Always store transient data in `UserSession` to avoid leaks between sessions.
141−
142−## REST Frameworks (Horse, DMVC, Dext)
143−
144−### Dext Framework
145−
146−Dext (<https://github.com/cesarliws/dext>) is an enterprise landmark inspired by the .NET ecosystem. Conventions:
147−
148−- **Minimal APIs:** Use `App.Builder.MapGet` with Auto-Binding for DTOs
149−- **Dependency Injection:** Mandatory. Inject into endpoints: `function(Dto: MyDto; Rep: ICustomerRepository): IResult`
150−- **Entity ORM:** LINQ type queries (`DbContext.Where(U.Age > 18)`). Do not use queries in pure SQL chained.
151−- **Async:** Use `TAsyncTask.Run` from `Dext.Core.Tasks`.
152−- **Results:** Return typed frameworks or Records directly, serialized as JSON.
153−
154−### Horse Framework
155−
156−Horse is a minimalist REST framework for Delphi (Express style). Conventions:
157−
158−- **Controller:** Class with `class procedure RegisterRoutes`
159−- **Handler:** `class procedure Nome(AReq: THorseRequest; ARes: THorseResponse; ANext: TProc)`
160−- **Middleware:** `procedure Nome(AReq: THorseRequest; ARes: THorseResponse; ANext: TProc)`
161−- **Routes:** Kebab-case, plural — `/api/customers`, `/api/order-items`
162−- **JSON:** Use `Jhonson` middleware for automatic serialization
163−- **CORS:** Use `Horse.CORS` middleware
164−- **Structure:** Controllers separated from Services, Services separated from Repositories
165−- **Packages:** `boss install horse horse-jhonson horse-cors horse-jwt`
166−
167−### DelphiMVCFramework (DMVC)
168−
169−DMVC is a classic MVC framework with Active Record, JWT and Swagger:
170−
171−- **Controller:** Inherits from `TMVCController` with `[MVCPath]` attribute
172−- **Routes:** Attributes — `[MVCPath]`, `[MVCHTTPMethod]`, `[MVCProduces]`, `[MVCConsumes]`
173−- **Active Record:** Inherits from `TMVCActiveRecord` with `[MVCTable]`, `[MVCTableField]`
174−- **Serialization:** Automatic via `Render()` (JSON by default)
175−- **WebModule:** `TMVCEngine` created in WebModule with controllers and middleware
176−- **JWT:** `TMVCJWTAuthenticationMiddleware` built-in
177−- **RQL:** Resource Query Language for filters via query string
178−
179−### DevExpress Components
180−
181−Advanced visual components for VCL:
182−
183−- **Grid:** `TcxGrid` with `TcxGridDBTableView` (data-aware)
184−- **Layout:** `TdxLayoutControl` for responsive forms
185−- **Skins:** `TdxSkinController` for global themes
186−- **Export:** `cxGridExportLink` to Excel/PDF
187−- **Filters:** `DataController.Filter` for programmatic filters
188−
189−## Firebird Database
190−
191−Firebird is the most used corporate database with Delphi. Access via **FireDAC** (driver `FB`).
192−
193−### Mandatory Connection Configuration
194−
195−```pascal
196−FConnection.DriverName := 'FB';
197−FConnection.Params.Values['CharacterSet'] := 'UTF8'; //ALWAYS UTF8
198−FConnection.Params.Values['SQLDialect'] := '3'; //NEVER Dialect 1
199−FConnection.Params.Values['Protocol'] := 'TCPIP'; //Or 'Local' for embedded
200−FConnection.Params.Values['PageSize'] := '16384'; // 16KB recomendado
201−FConnection.TxOptions.Isolation := xiReadCommitted; //Standard isolation
202−```
203−
204−### Essential Rules Firebird
205−
206−- **Dialect 3 ALWAYS** — Dialect 1 is InterBase legacy and causes ambiguity with `DATE`
207−- **CharacterSet UTF8** — required for correct accent support
208−- **Parameterized queries** — never concatenate strings in SQL
209−- **RETURNING with Open** — `INSERT ... RETURNING id` requires `LQuery.Open`, not `ExecSQL`
210−- **Generators** for auto-increment with `BEFORE INSERT` triggers
211−- **Domains** to centralize types and validations in the schema
212−- **Stored Procedures:** Selectable (with `SUSPEND`) uses `SELECT FROM SP`; Executable uses `EXECUTE PROCEDURE`
213−- **Explicit transactions** for compound operations (StartTransaction/Commit/Rollback)
214−- **Treat deadlocks** via `EFDDBEngineException.Kind = ekRecordLocked`
215−
216−### Firebird Anti-Patterns
217−
218−- ❌ `SQLDialect := '1'` — ALWAYS use `'3'`
219−- ❌ `ExecSQL` with `RETURNING` — use `Open`
220−- ❌ Concatenate SQL — use parameters
221−- ❌ Ignore `CharacterSet` — set `UTF8`
222−- ❌ `PAGE_SIZE 4096` — use `16384` for production
223−- ❌ Bypass deadlocks — handle `ekRecordLocked`
224−- ❌ `CREATE TABLE IF NOT EXISTS` — does not exist in Firebird (check via `RDB$RELATIONS`)
225−
226−> **Skills:** `.gemini/skills/firebird-database/SKILL.md`
227−> **Rules:** `.cursor/rules/firebird-patterns.md`
228−
229−## PostgreSQL Database
230−
231−PostgreSQL is the most advanced open-source database, ideal for modern projects. Access via **FireDAC** (driver `PG`).
232−
233−### Connection Configuration
234−
235−```pascal
236−FConnection.DriverName := 'PG';
237−FConnection.Params.Values['Server'] := 'localhost';
238−FConnection.Params.Values['Port'] := '5432';
239−FConnection.Params.Database := 'meubanco';
240−FConnection.Params.UserName := 'postgres';
241−FConnection.Params.Password := 'senha';
242−FConnection.Params.Values['CharacterSet'] := 'UTF8';
243−FConnection.TxOptions.Isolation := xiReadCommitted;
244−```
245−
246−### Essential Rules PostgreSQL
247−
248−- **IDENTITY instead of SERIAL** — use `GENERATED ALWAYS AS IDENTITY` for new projects (PG 10+)
249−- **RETURNING with Open** — `INSERT ... RETURNING id` requires `LQuery.Open`, not `ExecSQL`
250−- **native UPSERT** — `INSERT ... ON CONFLICT (col) DO UPDATE SET ...`
251−- **JSONB** — for semi-structured data, indexable with GIN
252−- **ENUM types** — `CREATE TYPE status AS ENUM ('active', 'inactive')` mapped to Pascal enum
253−- **PL/pgSQL** — Functions (`RETURNS TABLE` = Selectable), Procedures (`CALL`, PG 11+)
254−- **Explicit transactions** — `StartTransaction/Commit/Rollback`, supports `SAVEPOINT`
255−- **Full-Text Search** — `tsvector` + `tsquery` with GIN index
256−- **Metadata via `information_schema`** — do not use `RDB$` (this is Firebird)
257−
258−### PostgreSQL Anti-Patterns
259−
260−- ❌ Concatenate SQL — use parameterized parameters
261−- ❌ `ExecSQL` with `RETURNING` — use `Open`
262−- ❌ `SERIAL` in new projects — use `IDENTITY`
263−- ❌ `SELECT *` in large tables — select required columns
264−- ❌ N+1 queries — use JOIN or subquery
265−- ❌ Save JSON as TEXT — use `JSONB`
266−- ❌ Ignore indexes on WHERE/JOIN columns
267−
268−> **Skills:** `.gemini/skills/postgresql-database/SKILL.md`
269−> **Rules:** `.cursor/rules/postgresql-patterns.md`
270−
271−## MySQL / MariaDB Database
272−
273−MySQL is the most popular open-source database in the world. MariaDB is a compatible fork. Access via **FireDAC** (driver `MySQL`).
274−
275−### Connection Configuration
276−
277−```pascal
278−FConnection.DriverName := 'MySQL';
279−FConnection.Params.Values['Server'] := 'localhost';
280−FConnection.Params.Values['Port'] := '3306';
281−FConnection.Params.Database := 'meubanco';
282−FConnection.Params.UserName := 'root';
283−FConnection.Params.Password := 'senha';
284−FConnection.Params.Values['CharacterSet'] := 'utf8mb4'; //NEVER 'utf8' (only 3 bytes!)
285−FConnection.TxOptions.Isolation := xiReadCommitted;
286−```
287−
288−### Essential Rules MySQL
289−
290−- **`utf8mb4` ALWAYS** — `utf8` in MySQL only has 3 bytes (does not support emoji). Use `utf8mb4`
291−- **AUTO_INCREMENT + LAST_INSERT_ID()** — MySQL does NOT support `RETURNING`. Get ID via `LAST_INSERT_ID()`
292−- **native UPSERT** — `INSERT ... ON DUPLICATE KEY UPDATE`
293−- **Native JSON** — `JSON` type with `->>`/`JSON_EXTRACT` operators (MySQL 5.7+)
294−- **InnoDB ALWAYS** — never MyISAM in new projects (needs FK and transactions)
295−- **Stored Procedures** — `CALL sp_nome(...)` for procedures, `SELECT fn_nome(...)` for functions
296−- **Explicit transactions** — `StartTransaction/Commit/Rollback`, supports `SAVEPOINT`
297−- **COLLATE** — `utf8mb4_unicode_ci` for correct case-insensitive comparison
298−- **Metadata via `information_schema`** — use `DATABASE()` for current schema
299−
300−### MySQL Anti-Patterns
301−
302−- ❌ `utf8` as charset — use `utf8mb4`
303−- ❌ Try `RETURNING` — does not exist, use `LAST_INSERT_ID()`
304−- ❌ `MyISAM` in new tables — use `InnoDB`
305−- ❌ Concatenate SQL — use parameters
306−- ❌ `SELECT *` without `LIMIT` — page results
307−- ❌ N+1 queries — use JOIN or subquery
308−- ❌ Ignore indexes on WHERE/JOIN columns
309−
310−> **Skills:** `.gemini/skills/mysql-database/SKILL.md`
311−> **Rules:** `.cursor/rules/mysql-patterns.md`
312−
313−## Threads and Multi-Threading
314−
315−Threads are essential for keeping the UI responsive and processing data in parallel. Delphi offers `TThread`, PPL (`TTask`, `TParallel.For`, `TFuture<T>`) and synchronization primitives.
316−
317−### Golden Rule
318−
319−> **NEVER access visual components (VCL/FMX) directly from a secondary thread.**
320−> Use `TThread.Synchronize` (blocking) or `TThread.Queue` (non-blocking) to update the UI.
321−
322−### Threading Approaches
323−
324−| Approach | When to Use |
325−|-----------|-------------|
326−| `TThread.CreateAnonymousThread` | Simple, one-shot tasks |
327−| `TTask.Run` (PPL) | Modern way, managed pool |
328−| `TParallel.For` | Parallel loop in independent collections |
329−| `TFuture<T>` | Asynchronous result with return value |
330−| `TThread` (inheritance) | Permanent workers, queues, servers |
331−
332−### Thread-Safety
333−
334−- **`TCriticalSection`** — Classic critical section (`Enter`/`Leave` ALWAYS in `finally`)
335−- **`TMonitor`** — Native object lock (`Enter`/`Exit`)
336−- **`TInterlocked`** — Atomic operations (`Increment`, `Decrement`, `Exchange`)
337−- **`TThreadList<T>`** — Thread-safe list with `LockList`/`UnlockList`
338−- **`TMultiReadExclusiveWriteSynchronizer`** — Cache: multiple reads, few writes
339−- **`TThreadedQueue<T>`** — Thread-safe queue for Producer-Consumer
340−
341−### Threading Anti-Patterns
342−
343−- ❌ Access VCL/FMX directly from secondary thread
344−- ❌ `Sleep()` in the main thread (freezes the UI!)
345−- ❌ `FreeOnTerminate := True` + `WaitFor` (crash!)
346−- ❌ Access shared variables without locking
347−- ❌ Ignore exceptions in threads (they are silent!)
348−- ❌ `TCriticalSection.Leave` fora de `finally`
349−
350−> **Skills:** `.gemini/skills/threading/SKILL.md`
351−> **Rules:** `.cursor/rules/threading-patterns.md`
352−
353−## SOLID principles in Delphi
354−
355−### S — Single Responsibility Principle (SRP)
356−
357−Each unit and each class must have **a single responsibility**:
358−
359−```pascal
360−//✅ GOOD — separate responsibilities
361−TCustomerValidator = class
362− function Validate(ACustomer: TCustomer): TValidationResult;
363−end;
364−
365−TCustomerRepository = class(TInterfacedObject, ICustomerRepository)
366− function FindById(AId: Integer): TCustomer;
367− procedure Save(ACustomer: TCustomer);
368−end;
369−
370−//❌ BAD — class doing it all
371−TCustomer = class
372− procedure Validate; //should be a Validator
373− procedure SaveToDb; //should be a Repository
374− procedure SendEmail; //should be a Service
375−end;
376−```
377−
378−### O — Open/Closed Principle (OCP)
379−
380−Classes should be **open for extension**, closed for modification. Use inheritance and interfaces:
381−
382−```pascal
383−type
384− IReportExporter = interface
385− procedure Export(AReport: TReport);
386− end;
387−
388− TPdfExporter = class(TInterfacedObject, IReportExporter)
389− procedure Export(AReport: TReport);
390− end;
391−
392− TExcelExporter = class(TInterfacedObject, IReportExporter)
393− procedure Export(AReport: TReport);
394− end;
395−```
396−
397−### L — Liskov Substitution Principle (LSP)
398−
399−Subtypes must be replaceable with the base type without breaking behavior:
400−
401−```pascal
402−//✅ GOOD — any ICustomerRepository works
403−procedure TCustomerService.LoadCustomer(ARepo: ICustomerRepository);
404−begin
405− //works with TFireDACCustomerRepo, TMemoryCustomerRepo, TMockCustomerRepo
406− FCustomer := ARepo.FindById(FCustomerId);
407−end;
408−```
409−
410−### I — Interface Segregation Principle (ISP)
411−
412−Small, cohesive interfaces, not "fat" interfaces:
413−
414−```pascal
415−//✅ GOOD — segregated interfaces
416−type
417− IReadableRepository<T> = interface
418− function FindById(AId: Integer): T;
419− function FindAll: TObjectList<T>;
420− end;
421−
422− IWritableRepository<T> = interface
423− procedure Save(AEntity: T);
424− procedure Delete(AId: Integer);
425− end;
426−
427− ICustomerRepository = interface(IReadableRepository<TCustomer>)
428− ['{GUID}']
429− function FindByCpf(const ACpf: string): TCustomer;
430− end;
431−```
432−
433−### D — Dependency Inversion Principle (DIP)
434−
435−Depend on **abstractions** (interfaces), not concrete implementations. Use **constructor injection**:
436−
437−```pascal
438−type
439− TOrderService = class
440− private
441− FOrderRepo: IOrderRepository;
442− FNotifier: INotificationService;
443− public
444− constructor Create(AOrderRepo: IOrderRepository; ANotifier: INotificationService);
445− procedure PlaceOrder(AOrder: TOrder);
446− end;
447−
448−constructor TOrderService.Create(AOrderRepo: IOrderRepository; ANotifier: INotificationService);
449−begin
450− inherited Create;
451− FOrderRepo := AOrderRepo;
452− FNotifier := ANotifier;
453−end;
454−```
455−
456−## Clean Code — Essential Rules
457−
458−### 1. Short Methods
459−
460−- Maximum **20 lines** per method (ideal: 5-10)
461−- If a method needs a comment explaining "what it does", it should be extracted into a method with a descriptive name
462−
463−### 2. Self-Descriptive Names
464−
465−```pascal
466−//❌ SPACIOUS
467−procedure Proc1(S: string; N: Integer);
468−function Calc(V: Double): Double;
469−
470−// ✅ BOM
471−procedure SendNotificationEmail(const ARecipientEmail: string; ATemplateId: Integer);
472−function CalculateDiscountedPrice(AOriginalPrice: Double): Double;
473−```
474−
475−### 3. Avoid Magic Numbers
476−
477−```pascal
478−//❌ SPACIOUS
479−if ACustomer.Age > 18 then
480−
481−// ✅ BOM
482−const
483− MINIMUM_AGE = 18;
484−// ...
485−if ACustomer.Age > MINIMUM_AGE then
486−```
487−
488−### 4. Guard Clauses
489−
490−```pascal
491−//❌ BAD — excessive nesting
492−procedure ProcessOrder(AOrder: TOrder);
493−begin
494− if Assigned(AOrder) then
495− begin
496− if AOrder.Items.Count > 0 then
497− begin
498− if AOrder.IsValid then
499− begin
500− //real logic here
501− end;
502− end;
503− end;
504−end;
505−
506−//✅ BOM — guard clauses
507−procedure ProcessOrder(AOrder: TOrder);
508−begin
509− if not Assigned(AOrder) then
510− raise EArgumentNilException.Create('AOrder cannot be nil');
511− if AOrder.Items.Count = 0 then
512− raise EBusinessRuleException.Create('Order must have at least one item');
513− if not AOrder.IsValid then
514− raise EValidationException.Create('Order validation failed');
515−
516− //real logic here — no nesting
517−end;
518−```
519−
520−### 5. Focused and Typed Try/Except
521−
522−```pascal
523−//❌ BAD — generic catch swallowing critical errors (Access Violation, OOM)
524−try
525− //large block of long code
526−except
527− on E: Exception do //Or worse: without declaring "on E:"
528− ShowMessage(E.Message);
529−end;
530−
531−//✅ GOOD — specific exceptions and granular recovery
532−try
533− FConnection.Open;
534− PerformCriticalAction;
535−except
536− on E: EFDDBEngineException do
537− raise EDatabaseConnectionException.Create('Falha local no banco: ' + E.Message);
538− on E: EBusinessRuleException do
539− raise; //Pass the exception to the Controller to catch
540− on E: Exception do
541− begin
542− Logger.LogError('Critical unexpected failure', E);
543− raise; //NEVER hide pure root Exception exceptions without rethrowing!
544− end;
545−end;
546−```
547−
548−### 6. Unit Organization
549−
550−```pascal
551−unit MyApp.Domain.Customer.Entity;
552−
553−interface
554−
555−uses
556− System.SysUtils,
557− System.Classes,
558− System.Generics.Collections;
559−
560−type
561− //1. Types, enums and records first
562− TCustomerStatus = (csActive, csInactive, csSuspended);
563−
564− //2. Interfaces
565− ICustomer = interface
566− ['{GUID}']
567− function GetName: string;
568− property Name: string read GetName;
569− end;
570−
571− //3. Classes
572− TCustomer = class(TInterfacedObject, ICustomer)
573− private
574− FId: Integer;
575− FName: string;
576− FStatus: TCustomerStatus;
577− function GetName: string;
578− public
579− //Constructor and Destructor first
580− constructor Create(const AName: string);
581− destructor Destroy; override;
582−
583− //After public methods
584− function IsActive: Boolean;
585− procedure Activate;
586− procedure Deactivate;
587−
588− //Properties last
589− property Id: Integer read FId write FId;
590− property Name: string read GetName;
591− property Status: TCustomerStatus read FStatus;
592− end;
593−
594−implementation
595−
596−{ TCustomer }
597−
598−constructor TCustomer.Create(const AName: string);
599−begin
600− inherited Create;
601− if AName.Trim.IsEmpty then
602− raise EArgumentException.Create('Customer name cannot be empty');
603− FName := AName.Trim;
604− FStatus := csActive;
605−end;
606−
607−//... other implementations
608−```
609−
610−## Recommended Design Patterns
611−
612−| Standard | Use in Delphi |
613−|--------|---------------|
614−| **Repository** | Abstracts data access via interface (FireDAC, REST, etc.) |
615−| **Service** | Contains business logic orchestrating repositories and other services |
616−| **Factory** | Creates instances of complex objects or with dependencies |
617−| **Observer** | Use `TNotifyEvent` or interfaces to decouple notifications |
618−| **Strategy** | Interfaces to vary algorithms (e.g. tax calculation) |
619−| **Unit of Work** | Manages database transactions |
620−
621−## Anti-Patterns to Avoid
622−
623−- ❌ **God class / God unit** — units with thousands of lines doing everything
624−- ❌ **Direct coupling to forms** — business logic in `OnClick` of buttons
625−- ❌ **Uses circular** — resolved by separating into layers (Domain, Infra, Application, Presentation)
626−- ❌ **Global variables** — use dependency injection
627−- ❌ **Hardcoded Strings** — use `resourcestring` or constants
628−- ❌ **Ignoring memory management** — always free unmanaged objects by reference
629−- ❌ **`with` statement** — avoid `with` as it reduces readability and makes debugging difficult
630−- ❌ **Testing in Banco Real** — attach DUnitX projects directly to `TFDConnection`, skipping Mocks/Fakes.
631−
632−## Memory Management (Critical)
633−
634−- **Watched Blocks:** The golden rule in Delphi: Whenever there is code calling `.Create` for instances of TObject Classes, the IMMEDIATELY subsequent line must be a `try`. NO intermediate lines of code!
635−
636−```pascal
637−//✅ The Gold Standard for Disposable Objects
638−var LList: TStringList;
639−begin
640− LList := TStringList.Create;
12+## Crucial Directives (Memory Management)
13+- **Watched Blocks (Required):** EVERYTHING you instantiate with `.Create` (if it is `TObject` and does not have `Owner`) **MUST** have a `try..finally` on the IMMEDIATELY subsequent line.
14+ ```pascal
15+ Obj := TMyClass.Create;
64116 try
642− LList.Add('item');
643− // ...
17+ Obj.DoSomething;
64418 finally
645− LList.Free; //i FreeAndNil(LList)
19+ Obj.Free; //my FreeAndNil(Obj)
64620 end;
647−end;
21+ ```
22+- **DO NOT use** `with`.
23+- **DO NOT create** God Classes. Use SOLID Principles.
24+- Isolate visual components (FMX/VCL) from strict business rules. Do not access DBGrid or form edits in pure logical units.
25+- For dependency injection, pass abstractions in the constructor.
64826
649−//✅ Objects with owner - VCL/FMX components
650−TMyComponent := TMyComponent.Create(Self); //Owner (Self) assumes release
27+## File Organization & Naming (PascalCase)
28+- Classes: Start with `T` (ex: `TCustomer`).
29+- Interfaces: Start with `I` (ex: `ICustomer`).
30+- Exceptions: Start with `E` (ex: `EValidationError`).
31+- Private attributes or fields: Start with `F` (ex: `FName`).
32+- Local variables: Start with `L` (ex: `LCustomer`).
33+- Parameters: Start with `A` (ex: `ACustomer`).
34+- Unit nomenclature: `NomeProjeto.Camada.Dominio.Funcionalidade.pas`
65135
652−//✅ Garbage Collection com Interfaces (ARC)
653−//The object will be automatically cleaned up at the end of the scope, eliminating the need for try..free
654−var LService: IMyService;
655−begin
656− LService := TMyService.Create;
657− LService.DoSomething;
658−end;
659−
660−//✅ Local variables: use L prefix
661−var LCustomer: TCustomer;
662−```
663−
664−## Documentation
665−
666−- Use **XMLDoc** for public methods and interfaces:
667−
668−```pascal
669−///<summary>
670−///Locates a customer by the CPF entered.
671−///</summary>
672−///<param name="ACpf">Customer CPF (numbers only)</param>
673−///<returns>TCustomer instance or nil if not found</returns>
674−///<exception cref="EArgumentException">If ACpf is empty</exception>
675−function FindByCpf(const ACpf: string): TCustomer;
676−```
677−
678−- Comments in **Portuguese** for Brazilian projects
679−- Don't comment obvious code — let the method name explain
680−
681−## Layer Structure (Architecture)
682−
683−```
684−src/
685−├── Domain/ ← Entidades, Value Objects, Interfaces de repositório
686−├── Application/ ← Services, Use Cases, DTOs
687−├── Infrastructure/ ← Implementações de repositório (FireDAC), APIs externas
688−└── Presentation/ ← Forms (VCL/FMX), ViewModels
689−tests/
690−└── Unit/ ← Projetos DUnitX e Fakes/Mocks isolados por contexto
691−```
692−
693−> **Dependency rule:** `Presentation → Application → Domain ← Infrastructure`
694−> The Domain **never** depends on other layers. `tests` depend on `Application` and `Domain` but inject Fake implementations by copying `Infrastructure` alone.
695−
696−---
697−
698−## 🚫 AI Context Policy — What to Include and Exclude
699−
700−> Full strategy documented in `docs/ai-ignore-strategy.md`.
701−
702−### Files AI Must Always Use as Context
703−
704−- `AGENTS.md` — universal rules
705−- `README.md` — project overview
706−- `.github/copilot-instructions.md` — Copilot pre-prompt
707−- `.claude/CLAUDE.md` — Claude master prompt
708−- `.claude/rules/**/*.md` — context-specific rules
709−- `.claude/skills/**/SKILL.md` — on-demand skills
710−- `.cursor/rules/**/*.md` — Cursor rules
711−- `.gemini/skills/**/SKILL.md` — Gemini skills
712−- `.kiro/steering/**/*.md` — Kiro steering docs
713−- `examples/**/*.pas` — good practice examples
714−- `docs/**/*.md` — documentation
715−
716−### Files AI Must Never Use as Context
717−
718−- Build artifacts: `*.dcu`, `*.exe`, `*.dll`, `*.bpl`, `*.dcp`, `*.map`, `*.res`
719−- IDE temporaries: `*.local`, `*.identcache`, `*.stat`, `__history/`, `__recovery/`
720−- Output directories: `Win32/`, `Win64/`, `Debug/`, `Release/`, `build/`, `dist/`
721−- Secrets: `*.key`, `*.pfx`, `*.p12`, `.env`, `.env.*`
722−- Noise: `*.log`, `*.dmp`, `*.bak`, `*.tmp`
723−
724−See `.cursorignore`, `.gitignore` and `.vscode/settings.json` for the enforced patterns.
725−
36+*(See the `AGENTS.md` global file and `rules/` folder for guidelines specific to frameworks such as FireDAC, Rest, Horse and Database).*
72637
