

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
1# Delphi AI Spec-Kit — AGENTS.md23> This file is automatically recognized by **Antigravity**, **GitHub Copilot**, **Cursor** and **Kiro**.4> It defines the universal rules for Delphi development with AI.56## Language and Stack78- **Language:** Object Pascal (Delphi)9- **Native IDE:** RAD Studio / Delphi10- **Frameworks:** VCL, FMX, FireDAC11- **Database:** FireDAC (SQLite, PostgreSQL, Firebird, SQL Server)12- **Tests:** DUnitX13- **Build:** MSBuild / Delphi Compiler (dcc32/dcc64)14- **File extensions:** `.pas` (units), `.dfm`/`.fmx` (forms), `.dpr` (project), `.dpk` (package), `.dproj` (project config)1516## Naming Conventions — Pascal Guide1718### General Rule1920Use **PascalCase** (InfixCaps) for all identifiers. Reserved words are always in **TOK_6__, `end`, `if`, `then`, `else`, `nil`, `string`).2122### Mandatory Prefixes2324| 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` |3334### Unit Naming3536```37NomeProjeto.Camada.Dominio.Funcionalidade.pas38```3940Examples:4142- `MyApp.Domain.Customer.Entity.pas`43- `MyApp.Infra.Customer.Repository.pas`44- `MyApp.Application.Customer.Service.pas`45- `MyApp.Presentation.Customer.View.pas`4647### Method Naming4849- 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`5354### Unit Test Naming (TDD)5556- 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`)5960### Naming of Forms and DataModules6162- Type: `TfrmCustomerEdit`, `TdmDatabase`63- Variable: `frmCustomerEdit`, `dmDatabase`64- Unit: `MyApp.Presentation.Customer.Edit.pas`6566### Components in Forms6768Use a 3-letter prefix indicating the type:6970| 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` |9495### DevExpress (DEXT) components in Forms9697| 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` |113114### ACBr Project (Commercial Automation)115116| 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` |125126**ACBr Note:** Avoid trapping the UI directly in component interactive events. Isolate tax logic.127128### Intraweb Components (Web)129130| 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` |139140**Intraweb Note:** Avoid global unit variables to control user state. Always store transient data in `UserSession` to avoid leaks between sessions.141142## REST Frameworks (Horse, DMVC, Dext)143144### Dext Framework145146Dext (<https://github.com/cesarliws/dext>) is an enterprise landmark inspired by the .NET ecosystem. Conventions:147148- **Minimal APIs:** Use `App.Builder.MapGet` with Auto-Binding for DTOs149- **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.153154### Horse Framework155156Horse is a minimalist REST framework for Delphi (Express style). Conventions:157158- **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 serialization163- **CORS:** Use `Horse.CORS` middleware164- **Structure:** Controllers separated from Services, Services separated from Repositories165- **Packages:** `boss install horse horse-jhonson horse-cors horse-jwt`166167### DelphiMVCFramework (DMVC)168169DMVC is a classic MVC framework with Active Record, JWT and Swagger:170171- **Controller:** Inherits from `TMVCController` with `[MVCPath]` attribute172- **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 middleware176- **JWT:** `TMVCJWTAuthenticationMiddleware` built-in177- **RQL:** Resource Query Language for filters via query string178179### DevExpress Components180181Advanced visual components for VCL:182183- **Grid:** `TcxGrid` with `TcxGridDBTableView` (data-aware)184- **Layout:** `TdxLayoutControl` for responsive forms185- **Skins:** `TdxSkinController` for global themes186- **Export:** `cxGridExportLink` to Excel/PDF187- **Filters:** `DataController.Filter` for programmatic filters188189## Firebird Database190191Firebird is the most used corporate database with Delphi. Access via **FireDAC** (driver `FB`).192193### Mandatory Connection Configuration194195```pascal196FConnection.DriverName := 'FB';197FConnection.Params.Values['CharacterSet'] := 'UTF8'; //ALWAYS UTF8198FConnection.Params.Values['SQLDialect'] := '3'; //NEVER Dialect 1199FConnection.Params.Values['Protocol'] := 'TCPIP'; //Or 'Local' for embedded200FConnection.Params.Values['PageSize'] := '16384'; // 16KB recomendado201FConnection.TxOptions.Isolation := xiReadCommitted; //Standard isolation202```203204### Essential Rules Firebird205206- **Dialect 3 ALWAYS** — Dialect 1 is InterBase legacy and causes ambiguity with `DATE`207- **CharacterSet UTF8** — required for correct accent support208- **Parameterized queries** — never concatenate strings in SQL209- **RETURNING with Open** — `INSERT ... RETURNING id` requires `LQuery.Open`, not `ExecSQL`210- **Generators** for auto-increment with `BEFORE INSERT` triggers211- **Domains** to centralize types and validations in the schema212- **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`215216### Firebird Anti-Patterns217218- ❌ `SQLDialect := '1'` — ALWAYS use `'3'`219- ❌ `ExecSQL` with `RETURNING` — use `Open`220- ❌ Concatenate SQL — use parameters221- ❌ Ignore `CharacterSet` — set `UTF8`222- ❌ `PAGE_SIZE 4096` — use `16384` for production223- ❌ Bypass deadlocks — handle `ekRecordLocked`224- ❌ `CREATE TABLE IF NOT EXISTS` — does not exist in Firebird (check via `RDB$RELATIONS`)225226> **Skills:** `.gemini/skills/firebird-database/SKILL.md`227> **Rules:** `.cursor/rules/firebird-patterns.md`228229## PostgreSQL Database230231PostgreSQL is the most advanced open-source database, ideal for modern projects. Access via **FireDAC** (driver `PG`).232233### Connection Configuration234235```pascal236FConnection.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```245246### Essential Rules PostgreSQL247248- **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 GIN252- **ENUM types** — `CREATE TYPE status AS ENUM ('active', 'inactive')` mapped to Pascal enum253- **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 index256- **Metadata via `information_schema`** — do not use `RDB$` (this is Firebird)257258### PostgreSQL Anti-Patterns259260- ❌ Concatenate SQL — use parameterized parameters261- ❌ `ExecSQL` with `RETURNING` — use `Open`262- ❌ `SERIAL` in new projects — use `IDENTITY`263- ❌ `SELECT *` in large tables — select required columns264- ❌ N+1 queries — use JOIN or subquery265- ❌ Save JSON as TEXT — use `JSONB`266- ❌ Ignore indexes on WHERE/JOIN columns267268> **Skills:** `.gemini/skills/postgresql-database/SKILL.md`269> **Rules:** `.cursor/rules/postgresql-patterns.md`270271## MySQL / MariaDB Database272273MySQL is the most popular open-source database in the world. MariaDB is a compatible fork. Access via **FireDAC** (driver `MySQL`).274275### Connection Configuration276277```pascal278FConnection.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```287288### Essential Rules MySQL289290- **`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 functions296- **Explicit transactions** — `StartTransaction/Commit/Rollback`, supports `SAVEPOINT`297- **COLLATE** — `utf8mb4_unicode_ci` for correct case-insensitive comparison298- **Metadata via `information_schema`** — use `DATABASE()` for current schema299300### MySQL Anti-Patterns301302- ❌ `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 parameters306- ❌ `SELECT *` without `LIMIT` — page results307- ❌ N+1 queries — use JOIN or subquery308- ❌ Ignore indexes on WHERE/JOIN columns309310> **Skills:** `.gemini/skills/mysql-database/SKILL.md`311> **Rules:** `.cursor/rules/mysql-patterns.md`312313## Threads and Multi-Threading314315Threads are essential for keeping the UI responsive and processing data in parallel. Delphi offers `TThread`, PPL (`TTask`, `TParallel.For`, `TFuture<T>`) and synchronization primitives.316317### Golden Rule318319> **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.321322### Threading Approaches323324| 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 |331332### Thread-Safety333334- **`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 writes339- **`TThreadedQueue<T>`** — Thread-safe queue for Producer-Consumer340341### Threading Anti-Patterns342343- ❌ Access VCL/FMX directly from secondary thread344- ❌ `Sleep()` in the main thread (freezes the UI!)345- ❌ `FreeOnTerminate := True` + `WaitFor` (crash!)346- ❌ Access shared variables without locking347- ❌ Ignore exceptions in threads (they are silent!)348- ❌ `TCriticalSection.Leave` fora de `finally`349350> **Skills:** `.gemini/skills/threading/SKILL.md`351> **Rules:** `.cursor/rules/threading-patterns.md`352353## SOLID principles in Delphi354355### S — Single Responsibility Principle (SRP)356357Each unit and each class must have **a single responsibility**:358359```pascal360//✅ GOOD — separate responsibilities361TCustomerValidator = class362 function Validate(ACustomer: TCustomer): TValidationResult;363end;364365TCustomerRepository = class(TInterfacedObject, ICustomerRepository)366 function FindById(AId: Integer): TCustomer;367 procedure Save(ACustomer: TCustomer);368end;369370//❌ BAD — class doing it all371TCustomer = class372 procedure Validate; //should be a Validator373 procedure SaveToDb; //should be a Repository374 procedure SendEmail; //should be a Service375end;376```377378### O — Open/Closed Principle (OCP)379380Classes should be **open for extension**, closed for modification. Use inheritance and interfaces:381382```pascal383type384 IReportExporter = interface385 procedure Export(AReport: TReport);386 end;387388 TPdfExporter = class(TInterfacedObject, IReportExporter)389 procedure Export(AReport: TReport);390 end;391392 TExcelExporter = class(TInterfacedObject, IReportExporter)393 procedure Export(AReport: TReport);394 end;395```396397### L — Liskov Substitution Principle (LSP)398399Subtypes must be replaceable with the base type without breaking behavior:400401```pascal402//✅ GOOD — any ICustomerRepository works403procedure TCustomerService.LoadCustomer(ARepo: ICustomerRepository);404begin405 //works with TFireDACCustomerRepo, TMemoryCustomerRepo, TMockCustomerRepo406 FCustomer := ARepo.FindById(FCustomerId);407end;408```409410### I — Interface Segregation Principle (ISP)411412Small, cohesive interfaces, not "fat" interfaces:413414```pascal415//✅ GOOD — segregated interfaces416type417 IReadableRepository<T> = interface418 function FindById(AId: Integer): T;419 function FindAll: TObjectList<T>;420 end;421422 IWritableRepository<T> = interface423 procedure Save(AEntity: T);424 procedure Delete(AId: Integer);425 end;426427 ICustomerRepository = interface(IReadableRepository<TCustomer>)428 ['{GUID}']429 function FindByCpf(const ACpf: string): TCustomer;430 end;431```432433### D — Dependency Inversion Principle (DIP)434435Depend on **abstractions** (interfaces), not concrete implementations. Use **constructor injection**:436437```pascal438type439 TOrderService = class440 private441 FOrderRepo: IOrderRepository;442 FNotifier: INotificationService;443 public444 constructor Create(AOrderRepo: IOrderRepository; ANotifier: INotificationService);445 procedure PlaceOrder(AOrder: TOrder);446 end;447448constructor TOrderService.Create(AOrderRepo: IOrderRepository; ANotifier: INotificationService);449begin450 inherited Create;451 FOrderRepo := AOrderRepo;452 FNotifier := ANotifier;453end;454```455456## Clean Code — Essential Rules457458### 1. Short Methods459460- 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 name462463### 2. Self-Descriptive Names464465```pascal466//❌ SPACIOUS467procedure Proc1(S: string; N: Integer);468function Calc(V: Double): Double;469470// ✅ BOM471procedure SendNotificationEmail(const ARecipientEmail: string; ATemplateId: Integer);472function CalculateDiscountedPrice(AOriginalPrice: Double): Double;473```474475### 3. Avoid Magic Numbers476477```pascal478//❌ SPACIOUS479if ACustomer.Age > 18 then480481// ✅ BOM482const483 MINIMUM_AGE = 18;484// ...485if ACustomer.Age > MINIMUM_AGE then486```487488### 4. Guard Clauses489490```pascal491//❌ BAD — excessive nesting492procedure ProcessOrder(AOrder: TOrder);493begin494 if Assigned(AOrder) then495 begin496 if AOrder.Items.Count > 0 then497 begin498 if AOrder.IsValid then499 begin500 //real logic here501 end;502 end;503 end;504end;505506//✅ BOM — guard clauses507procedure ProcessOrder(AOrder: TOrder);508begin509 if not Assigned(AOrder) then510 raise EArgumentNilException.Create('AOrder cannot be nil');511 if AOrder.Items.Count = 0 then512 raise EBusinessRuleException.Create('Order must have at least one item');513 if not AOrder.IsValid then514 raise EValidationException.Create('Order validation failed');515516 //real logic here — no nesting517end;518```519520### 5. Focused and Typed Try/Except521522```pascal523//❌ BAD — generic catch swallowing critical errors (Access Violation, OOM)524try525 //large block of long code526except527 on E: Exception do //Or worse: without declaring "on E:"528 ShowMessage(E.Message);529end;530531//✅ GOOD — specific exceptions and granular recovery532try533 FConnection.Open;534 PerformCriticalAction;535except536 on E: EFDDBEngineException do537 raise EDatabaseConnectionException.Create('Falha local no banco: ' + E.Message);538 on E: EBusinessRuleException do539 raise; //Pass the exception to the Controller to catch540 on E: Exception do541 begin542 Logger.LogError('Critical unexpected failure', E);543 raise; //NEVER hide pure root Exception exceptions without rethrowing!544 end;545end;546```547548### 6. Unit Organization549550```pascal551unit MyApp.Domain.Customer.Entity;552553interface554555uses556 System.SysUtils,557 System.Classes,558 System.Generics.Collections;559560type561 //1. Types, enums and records first562 TCustomerStatus = (csActive, csInactive, csSuspended);563564 //2. Interfaces565 ICustomer = interface566 ['{GUID}']567 function GetName: string;568 property Name: string read GetName;569 end;570571 //3. Classes572 TCustomer = class(TInterfacedObject, ICustomer)573 private574 FId: Integer;575 FName: string;576 FStatus: TCustomerStatus;577 function GetName: string;578 public579 //Constructor and Destructor first580 constructor Create(const AName: string);581 destructor Destroy; override;582583 //After public methods584 function IsActive: Boolean;585 procedure Activate;586 procedure Deactivate;587588 //Properties last589 property Id: Integer read FId write FId;590 property Name: string read GetName;591 property Status: TCustomerStatus read FStatus;592 end;593594implementation595596{ TCustomer }597598constructor TCustomer.Create(const AName: string);599begin600 inherited Create;601 if AName.Trim.IsEmpty then602 raise EArgumentException.Create('Customer name cannot be empty');603 FName := AName.Trim;604 FStatus := csActive;605end;606607//... other implementations608```609610## Recommended Design Patterns611612| 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 |620621## Anti-Patterns to Avoid622623- ❌ **God class / God unit** — units with thousands of lines doing everything624- ❌ **Direct coupling to forms** — business logic in `OnClick` of buttons625- ❌ **Uses circular** — resolved by separating into layers (Domain, Infra, Application, Presentation)626- ❌ **Global variables** — use dependency injection627- ❌ **Hardcoded Strings** — use `resourcestring` or constants628- ❌ **Ignoring memory management** — always free unmanaged objects by reference629- ❌ **`with` statement** — avoid `with` as it reduces readability and makes debugging difficult630- ❌ **Testing in Banco Real** — attach DUnitX projects directly to `TFDConnection`, skipping Mocks/Fakes.631632## Memory Management (Critical)633634- **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!635636```pascal637//✅ The Gold Standard for Disposable Objects638var LList: TStringList;639begin640 LList := TStringList.Create;641 try642 LList.Add('item');643 // ...644 finally645 LList.Free; //i FreeAndNil(LList)646 end;647end;648649//✅ Objects with owner - VCL/FMX components650TMyComponent := TMyComponent.Create(Self); //Owner (Self) assumes release651652//✅ Garbage Collection com Interfaces (ARC)653//The object will be automatically cleaned up at the end of the scope, eliminating the need for try..free654var LService: IMyService;655begin656 LService := TMyService.Create;657 LService.DoSomething;658end;659660//✅ Local variables: use L prefix661var LCustomer: TCustomer;662```663664## Documentation665666- Use **XMLDoc** for public methods and interfaces:667668```pascal669///<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```677678- Comments in **Portuguese** for Brazilian projects679- Don't comment obvious code — let the method name explain680681## Layer Structure (Architecture)682683```684src/685├── Domain/ ← Entidades, Value Objects, Interfaces de repositório686├── Application/ ← Services, Use Cases, DTOs687├── Infrastructure/ ← Implementações de repositório (FireDAC), APIs externas688└── Presentation/ ← Forms (VCL/FMX), ViewModels689tests/690└── Unit/ ← Projetos DUnitX e Fakes/Mocks isolados por contexto691```692693> **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.695696---697698## 🚫 AI Context Policy — What to Include and Exclude699700> Full strategy documented in `docs/ai-ignore-strategy.md`.701702### Files AI Must Always Use as Context703704- `AGENTS.md` — universal rules705- `README.md` — project overview706- `.github/copilot-instructions.md` — Copilot pre-prompt707- `.claude/CLAUDE.md` — Claude master prompt708- `.claude/rules/**/*.md` — context-specific rules709- `.claude/skills/**/SKILL.md` — on-demand skills710- `.cursor/rules/**/*.md` — Cursor rules711- `.gemini/skills/**/SKILL.md` — Gemini skills712- `.kiro/steering/**/*.md` — Kiro steering docs713- `examples/**/*.pas` — good practice examples714- `docs/**/*.md` — documentation715716### Files AI Must Never Use as Context717718- 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`723724See `.cursorignore`, `.gitignore` and `.vscode/settings.json` for the enforced patterns.725726
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| delphicleancode/delphi-spec-kit.claude/CLAUDE.md · 49 | CLAUDE.md | teststyleperformancedo-not | 60/100 | today | |
| delphicleancode/delphi-spec-kit.github/copilot-instructions.md · 49 | Copilot instructions | lint-formatstyledatabaseperformance+3 | 61/100 | today |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/delphicleancode-delphi-spec-kit-agents)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.