62 KiB
Sozsoft Platform - Claude Instructions
This file provides Claude-specific operating rules for this repository. Primary source of truth for platform behavior is:
.github/instructions/ai.instructions.md
If there is any conflict, follow .github/instructions/ai.instructions.md.
Communication Rules (apply to every response)
- Never introduce or summarize the platform. Assume the user knows what this application is, how it is built, and which technologies it uses. Do not open a response with "Sozsoft Platform is a multi-tenant low-code engine…" or any equivalent framing.
- Answer the request, nothing more. No unsolicited architecture overviews, no restating the decision order, no re-explaining ListForm/DeveloperKit concepts unless the user actually asked about them.
- No preamble, no epilogue. Skip "Great question", "I reviewed the codebase", summaries of what you are about to do, and closing recaps of what you just did.
- Match the question's size. A yes/no question gets a sentence. A config question gets the config. Only a full feature request gets a full proposal.
- Respond in the language the user writes in (Turkish → Turkish).
- Mention a rule from this file only when it changes the answer — never as boilerplate.
Purpose
- Maximize delivery through runtime configuration.
- Minimize custom code.
- Preserve platform consistency, security, and tenant isolation.
Primary principle: Configuration first, code last.
Mandatory Decision Order
For every request, evaluate in this order — internally. State the chosen step only when the user is asking how to build something, and state it in one line, not as a walkthrough of the options you rejected.
- Dynamic configuration with existing ListForm ecosystem
- SQL Query Manager + Custom Endpoint
- Dynamic Service
- Code change (last resort, justification required)
Non-Negotiable Rules
- Do not propose new custom React component/page development for standard feature requests.
- Build new screens using platform configuration mechanisms.
- Every implementation proposal must include tenant and permission design.
- Never bypass platform authorization patterns.
- Never hardcode secrets, tenant IDs, or connection strings.
- Never add a new Core feature (
api/,ui/code) without the command "core sisteme ekle" (standing default 11).
Exception:
- Custom React/backend code is written only on the user's command ("core sisteme ekle" or a request that names the code change) — never because configuration seemed insufficient.
- When Core would be needed, say why the ListForm inventory is not enough — in a sentence or two.
Architecture Guardrails
- Backend: Respect ABP module boundaries and explicit, auditable permissions.
- Frontend: Use existing dynamic view infrastructure and metadata-driven behavior.
- Data: Use parameterized SQL patterns and enforce tenant-safe access.
Dynamic Platform Expectations
- Menus and routes are database-driven.
- Dynamic List/Form/Component infrastructure is the default solution path.
- Keep route, menu, permission, and datasource mappings coherent.
Security and Compliance
- Enforce RBAC and permission-driven visibility in all layers.
- Never output real credentials, tokens, keys, or secrets.
- Use placeholders in examples.
- Maintain tenant isolation in every query and action.
Response Contract
The list below applies only to a full implementation proposal for a new screen, module, or integration. It is not a template for questions, debugging, code review, refactors, explanations, or small changes.
Even for a full proposal: include only the items that carry real content for that request, and drop the rest. An empty or obvious heading is noise.
- Goal
- Decision flow result (which step used)
- Artifacts to configure
- SQL/query/endpoint design (if needed)
- Menu + route + component mapping
- Permission and role mapping
- Tenant isolation notes
- Validation and test checklist
- Rollback strategy
Standing Context (read this before proposing anything)
These sections encode expectations the user will not repeat in every prompt.
README.md is the technical map of the platform; this file is how to act on it.
Where the truth lives
| Konu | Kaynak |
|---|---|
| Platform davranışı, karar sırası, çıktı sözleşmesi | .github/instructions/ai.instructions.md |
| Artefakt üretimi: seed şemaları, enum değerleri, çalışan örnekler | .github/instructions/lowcode.instructions.md |
| Yetenek envanteri: JSON kolonlarının alan alan sözleşmesi, grafik/pivot/tasarımcı/mail modülü, Saas + Administration + İK menüsündeki hazır ekranlar | .github/instructions/lowcode-reference.instructions.md |
api/ kod standardı (sürüm, nullable, ölü kod, modern C#, ABP katmanları) |
.github/instructions/dotnet.instructions.md |
| Modül/liste ekleme prosedürü, seeder dosya sorumlulukları | .github/instructions/list.instructions.md |
| Ne nerede yaşıyor, hangi ekran ne yapıyor | README.md |
| Bir modülün nasıl çalıştığı, analizi ve kararları | configs/seeds/docs/modules/{Menü}.md (dizin: README.md §10.5) |
| Yetki kodları | PlatformConsts.AppCodes (backend) + constants/permission.constant.ts (UI) |
| Seed yolları | SeedPathResolver |
| SQL Server → PostgreSQL çeviri kuralları | .github/instructions/lowcode.instructions.md §8.3.1 |
Bir çelişki varsa sıra: ai.instructions.md → dotnet.instructions.md → bu dosya → README.md.
Standing defaults — do not ask again
- Yeni tablo → tenant + tam audit kolonları varsayılan gelir (
Id,TenantId,CreationTime,CreatorId,LastModificationTime,LastModifierId,IsDeleted,DeletionTime,DeleterId). Kullanıcı açıkça "tenant yok / audit yok" demedikçe çıkarma. Tanım olmayan (işlem/belge) yeni tabloTenantIdgibiBranchId uniqueidentifier NOT NULL(PGuuid NOT NULL) da taşır; ekranıIsBranch: true. Şube alanı her ekranda aynı sözleşmeyle gelir (BranchIdkolonu taşıyan tanım ekranları dahil): seçenekler kullanıcıya atanmış aktif şubelerdir (Sas_T_BranchUsers, Kullanıcılar › Yetkiler › Şube Yönetimi;LookupQueryValues.UserBranchValues), yeni kayıt bu listenin ilk seçeneğiyle açılır (DbQuery,LookupQueryValues.UserDefaultBranchValueile aynı metin). Departman şubesi, sabit şube ya da ekrana özel sorgu yazılmaz. Bu tablodan okuyan view/rapor ekranı daBranchId'yi taşır veIsBranch: trueolur. Ana kayda bağlı tablo da kolonu taşır; formda önce Şube seçilir, ana kayıt lookup'ı şubeye göre süzülür (CascadeParentFields: BranchId) + alt form ilişkisiBranchId → BranchId. Ayrıntı:lowcode.instructions.md§8.1 (İşlem tablosu →BranchId). Yeni tanım tablosu (tanım olup olmadığına talebin içeriğine göre analizde karar verilir; sabit ad listesi yoktur) buna ek olarakIsActive bit NOT NULL DEFAULT 1ile gelir; liste + form alanı, varsayılantrue, Aktif/Pasif widget'ı. Tanım olmayan tabloya (işlem/belge tablosu, onun alt formları: satır, malzeme, süre, duruş, not)IsActiveeklenmez; yalnızca talep aktif/pasif bayrağını açıkça isterse aynı varsayılanla gelir: tablodaNOT NULL DEFAULT 1(create_table'daisNullable: false,defaultValue: "1"), formda yeni kayıttrueile açılır (FormFieldsDefaultValueJson). Ayrıntı:lowcode.instructions.md§8.1. Görsel tutan her kolon (fotoğraf, logo, avatar kopyası)nvarchar(MAX)+ editördxImageUpload; değer base64 saklanır, sınırlı boy vedxImageViewerkullanılmaz (deploy ikisini de reddeder). - Yeni ekran → Wizard yolu.
Definitionsaltına otomatik yerleştirme yok; modüle ait yeni bir kök menü oluştur,Order = max(Order) + 1. Yetki ağacı menü ağacını izler: ekranın okuma yetkisi menüdeki dalın ara yetkisinin altına yazılır; menüsüz alt formun yetkisi ise listenin yetkisine bağlanır (ParentName= listenin yetki kodu) (lowcode.instructions.md§0.12 Menü yeri). - Yeni ekran/menü → yetki sözleşmesi olmadan önerilmez. Menü ve route aynı ekran sözleşmesini göstermeli.
- Kullanıcıya görünen her metin → dil anahtarı üzerinden, EN + TR birlikte. Koda gömülü
metin önerme.
Kullanıcıya görünen her tarih ve sayı → aktif kültürle: UI'da
localeFormat.ts(formatLocaleDate/Time/DateTime/Number/Currency/Fixed), API'deCultureFormat+CultureInfo.CurrentCulture; DevExtreme kolon/editörü adlı biçim kullanır (shortDate,shortDateShortTime,shortTime,decimal,fixedPoint,currency). Sabit desen (DD.MM.YYYY,dd/MM/yyyy),'tr-TR'sabiti,toLocaleString()ve görüntülenen ondalık içintoFixedyazılmaz; makine biçimi (ISO yük,<input type=date>)ui/README.md'de. - Runtime'da üretilen her artefakt (wizard, custom component, crud endpoint) → ilgili seed dosyası da yazılır/güncellenir. Veritabanı sıfırlandığında geri gelmeyen bir şey üretme.
- Yeni ekran/komponent talebi — yazılı ya da görsel → dosya yazmadan önce keşif turu:
toplu sorularla (tur başına en fazla 4, seçenekli, önerilen varsayılanlı) ekran türü, veri
kaynağı, modül, kapsam ve yolun soru setini al; tarifi kur ve onaylat, sonra
configs/seeds/altına üret. Şüphede Wizard. Kullanıcı "sorma / sen seç" derse varsayılanlarla üret ve sonunda listele. Depodan okunabilen şeyi sorma; tabloya yazma hedefi, onaycı ve hesap formülü asla varsayılmaz. Ekran görüntüsü piksel sözleşmesi değildir; görsel Tur 2'nin çoğunu cevaplar, yalnızca görselden okunamayanlar sorulur. Bu kural yalnızca yeni üretim içindir; düzeltme/alan ekleme/debug doğrudan yapılır. Ayrıntı:lowcode.instructions.md§0.6–§0.7. - Ziyaretçiye açık yeni yol (public
RoutePathtaşıyan custom component,Pubmenüsü, yeni public sayfa) → SEO alanları da doldurulur. Ayrı bir SEO tablosu yoktur: meta, yolu tanımlayan kaydınSeo*kolonlarındadır —data/Sas.Menus.Routes.jsonya dadata/Sas.DeveloperKit.CustomComponents/{Id}.json. Metin::Anahtarile dil anahtarı taşır. Kullanıcı SEO bilgisi girmez; üreten yazar. Genel varsayılanlar kayıtta değilAdm.SiteManagement.Seo.*ayarlarındadır. Ayrıntı:lowcode.instructions.md§0.9. - Ekran içi hesap/koşul ihtiyacı → önce
EditorScript(Script Builder tarifleri), sonraEditorOptions; kod yazmak son çare. - SQL → her zaman parametreli. String birleştirme ile sorgu kurma.
- Silme →
DeleteCommand = DefaultDeleteCommand("{Tablo}"); ham SQL string'i yazma. - Core'a yeni özellik yalnızca komutla → Core =
api/+ui/kaynak kodu; çerçeve =configs/seeds/artefaktları. Kullanıcı "core sisteme ekle" (ya da Core değişikliğini adıyla isteyen eşdeğer komut) demedikçe Core'a yeni özellik yazılmaz — küçük/geriye dönük uyumlu olması fark etmez: DTO alanı, var olan bileşene prop/olay, tarif,EditorOptionsanahtarı, token, yeni React bileşeni/sayfası, çekirdek motor, entity/migration, platform tablosu şeması, paket,Routes, dış entegrasyon. "Core gerekiyor" demeden önce ListForm'un bütün yetenekleri taranır (lowcode-reference.instructions.md§0–§9,lowcode§4–§9). Komut yoksa çerçeve içindeki kısım bitirilir, son mesajda tek bir "Core gerektiren kısım" notu yazılır (soru kutusuyla Core önerilmez). Bildirilen hatanın yeni ayar eklemeden düzeltilmesi yeni özellik değildir. Komut yalnızca adı geçen değişikliği kapsar, sonraki talebe taşınmaz; eklenen yetenek envantere yazılır. "Yapılamaz" cevabı yoktur. Ayrıntı:lowcode.instructions.md§0.8. - Liste ekranı görünümleri → prompt/görsel/tarifle üretilen her liste ekranı
Grid+Card+Pivot+Chartaçık,DefaultLayout: "grid"ile gelir; "liste/grid" demek diğerlerini kapatmak değildir. Bir görünüm yalnızca istek onu açıkça dışarıda bıraktığındafalseyazılır. Tree/Gantt/Scheduler/Todo yalnızca istenince açılır. Ayrıntı:lowcode.instructions.md§9.8. - Liste ekranı widget'ları → Wizard ya da MCP
deploy_wizardile üretilen her List ekranı sorulmadan en az 3 kartlıkWidgetsile gelir; kartlar tablonun durum / boolean bayrak / tarih / tutar kolonlarından seçilir, başlıklar dil anahtarıdır (Sas.Widget.*önce). Kiracı tablosunda her kart"TenantId" = '@TENANTID'taşır;IsDeletedkoşulu yazılmaz. Form/Chart, alt form ve basit tanım listeleri istisnadır. Alt form oluşturulurken widget oluşturulmaz: başka bir ekranınSubFormssekmesi olarak üretilen her ekran — menüsüz ya da kendi menüsü olan —Widgets: []ile deploy edilir. Deploy menüsüz (CreateMenu: false) wizard'ınWidgets'ını ayrıca düşürür; ekran alt form sekmesinde açıldığında şerit de çizilmez (useListFormWidgets(…, isSubForm)) — kart sorgusu ana kayda göre süzülmez. Ayrıntı:lowcode.instructions.md§4.6.1. Alt form ekranının menü/yetki/düzenleme kuralları (menüsüz alt formun yetkisi ana ekranın altında,Formsekmesi tek kayıt +AllowAdding/AllowUpdatingaçık):lowcode.instructions.md§4.6. - Modül dokümanı → MCP ya da wizard ile oluşturulan her modül çalışma şeklini
configs/seeds/docs/modules/{Menü}.mddosyasında taşır: dosya kök menü başına tektir ve adı kök menünün kodudur (Hr.md,Prc.md,Adm.md), menünün altındaki her dal dosyada birmodule-doc:sectionbölümüdür. Bölüm keşif cevapları ve onaylanan tarifi (analiz), ekranları, veri modelini, hesapları/durum geçişlerini, yetki dağılımını, entegrasyonları, sık sorulanları taşır. Dosyayı, dalın bölümünü ve "Ek — Ekran envanteri"ni deploy kendisi açar/yeniler; anlatım alt bölümlerini (module-doc:todo) üreten doldurur — MCP'desave_module_doc(ekran/dal koduyla yalnızca o bölüm), Claude Code'da dosyaya doğrudan. MCP talebi bölümü tamamlanmadanTesting'e alınamaz. Wizard dışı modülün bölümü elle yazılır. README'ye yalnızca §10.5 dizin satırı/bölüm adı ve ilgili bölümde tek cümlelik bağlantı yazılır. "X nasıl çalışıyor?" sorusunda önce bu dosya okunur. Ayrıntı:lowcode.instructions.md§0.13. - Ekran açıklaması → wizard/MCP ile üretilen her liste ekranının (alt formlar dahil)
DescTr/DescEnmetni ayrıntılıdır; başlıktaki ⓘ simgesinde gösterilir: ne işe yaradığı, menüde nerede / hangi ekranın sekmesi olduğu, ana alanlar, otomatik hesap ve geçişler, ilişkili ekranlar (RichTextbiçimi). MCPdeploy_wizard3 satır / 150 karakterin altını reddeder. Ayrıntı:lowcode.instructions.md§4.4.2. - İki veritabanı sağlayıcısı → uygulama SQL Server ile olduğu gibi PostgreSQL ile de çalışır
(
SettingsConsts.DefaultDatabaseProvider). Veritabanına dokunan her değişiklik ikisi için birlikte düşünülür ve yazılır; kullanıcı bunu her istekte hatırlatmaz:configs/seeds/sql/{object,execute,host/…}/X.sqlyazılan/değişen her dosyanınconfigs/seeds/postgres/…/X.sqleşi aynı işte yazılır/güncellenir (aynı ad, aynı parametre sırası, şema PG'depublic). Biri olmadan diğeri commit edilmez; "PG sonra" yoktur.- EF modeli SQL Server sözdizimiyle yazılır (kolon tipi, index filtresi, check, varsayılan); PG
karşılığı
PostgreSqlModelSyntaxüzerindenPlatformDbContext.ConfigureProviderSpecificModel'de üretilir. Çeviriye girmeyen yeni bir sözdizimi eklenirse çeviri de eklenir. - C#'ta üretilen SQL (Dapper,
QueryManager, seeder komutları, ekran*Command/LookupQuerymetinleri) ya iki sağlayıcıda da çalışan tırnaklı/ANSI biçimde yazılır ya daDataSourceType'a göre dallanır. - SQL değişikliği doğrulanırken PG eşi de bir PostgreSQL veritabanında çalıştırılır (iki kez —
idempotent). Ayrıntı ve dönüşüm tablosu:
lowcode.instructions.md§8.3.1.
- Her prompt sonunda optimizasyon + ölü kod temizliği → kullanıcı "optimize et, ölü kodları
kaldır" yazmaz; her iş bitmeden, kullanıcı istemese de o işte dokunulan kod üzerinde yapılır:
kullanılmayan
using/üye/parametre/tip/dil anahtarı silinir (tip silmeden önce repo geneli tarama, "Definition of done"), aynı mantığın ikinci kopyası ortak metoda indirilir, gereksiz tekrar sorgu/dosya okuması kaldırılır, kod/değer statik yazılmaz — tablo adıTableNameResolver'dan, değer tablo/seed/ayar verisinden dinamik bulunur. Son mesajda ne temizlendiği tek satırla söylenir. Kapsam seed artefaktlarını da içerir: yeni bir yordam/view/ekran eskisinin işini devralınca eskisi aynı işte silinir (iki sağlayıcının dosyası, wizard'ı, menü/yetki/dil satırları). Kullanımı biten SQL nesnesi (tablo, view, yordam, fonksiyon), menüsüz ve referanssız wizard, ekranı olmayandata/dosyası ve hiçbir ekran öğesinin göstermediği dil anahtarı bırakılmaz; tarama kuralılowcode.instructions.md§8.6. - Yeni kaydın form varsayılanı → Wizard ve MCP ile üretilen her listede form varsayılanı
kolonun veritabanındaki sabit
DEFAULTdeğeridir; deploy onu tablodan (katalog ya da seed scripti) okuyupFormFieldsDefaultValueJson'a yazar, değer koda gömülmez. Bu yüzden tablo şuDEFAULT'larla kurulur:IsActive→1(true), ölçü birimi (*UomId) →'Adet', para birimi (*CurrencyId/*Currency) →'TRY'(lookup'ın anahtarı neyse); talepte başka sabit varsayılan varsa (Süre = 60) o da kolonunDEFAULT'udur. Alana özel değer wizard öğesininDefaultValue'su ile verilir. Ayrıntı:lowcode.instructions.md§4.3.5.1. - Sabit lookup seçenekleri → RadioGroup/SelectBox/TagBox/Autocomplete'in
StaticDataseçeneklerindenamedüz metin değilSas.StaticLookup.*dil anahtarıdır; EN + TR satırı dil dosyasına yazılır. Deploy düz metni kendisi anahtara çevirir (aynı TR metinli anahtar varsa onu kullanır) —ListFormWizardAppService.LocalizeStaticLookupsAsync,lowcode.instructions.md§4.3.4.1. - EditorScript → MCP/wizard script'i elle yazmaz, Script Builder kurallarını verir; deploy kodu
kurallardan üretir (
EditorScript.Rebuild).str/num/boolalan adı alır (str('X'), değeri değil).setValue'datrue/falsemantıksal yazılır (onay kutusu alanında0/1deploy'da çevrilir). MCP ve Script Builder aynı kuralları aynı koda çevirir;setzinciri derinlik sınırsızdır, döngü ve script hatası kullanıcıya bildirilir. Ayrıntı:lowcode.instructions.md§4.4, §5.3, §5.6. - Demo veri ≠ seed senkronu →
SeedData: trueyalnızcaSeedFilePathaçar;SeedSyncInsert/Update/Deleteistekte açıkça istenmedikçefalse'tur (wizard cevabıSeedSync). - Wizard adı modül önekli → yeni wizard'ın adı (= seed dosyasının adı)
{ModuleId}_{Ad}'dır (Prc_RejectReason.json,Hr_RejectionReason.json); aynı ekran adı başka modülde de geçebildiği için öneksiz ad dosya çakışması ('X' adında bir Wizard zaten var) üretir. Önek deploy'daMenuParentModuleId'den eklenir (ListFormWizardDto.ResolveWizardName); analiz ve üretim planında dosya adı önekli yazılır.configs/seeds/wizard/altındaki bütün dosyalar bu düzendedir; öneksiz dosya yalnızca eski bir export arşivinden gelebilir ve düzenlenirken adı korunur. Ayrıntı:lowcode.instructions.md§4.10 (Adım 2 — Menü). - Otomatik numara Sequence'tan → wizard/MCP üretiminde talep otomatik numara istediğinde ya da
analiz bir alanın (
ProjectNo,OrderNo,RequestNo…) otomatik numaralanmasına karar verdiğinde alanAdm.Definitions.Sequencenumaratörüne bağlanır; elle girilen numara/kod alanı bu kurala girmez. Numaratördata/Adm.Definitions.Sequence.json'a satır olarak yazılır (uygun olan varsa yeniden kullanılır), alanın varsayılanıFormFieldsDefaultValueDto'daCustomValueType: 5,Value= numaratörünName'i.@AUTONUMBER,MAX + 1sorgusu ya da SQL ile numara üretilmez (boşluksuz numara açıkça istenmedikçe). Ayrıntı:lowcode.instructions.md§4.3.5.2. - İş akışlı liste → tablo, tasarımcının Workflow şablonundaki dört kolonla kurulur ve
WorkflowDtoyalnız bunlara bağlanır:ApprovalUserNamenvarchar(256),ApprovalStatusnvarchar(50),ApprovalDatedatetime,ApprovalDescriptionnvarchar(200) (PG: varchar/timestamp). Ayrı durum kolonu (Status), Guid onaycı kolonu (ApproverId) ve red nedeni kolonu açılmaz: durum akışın düğüm başlığıdır, red nedeni onay düğümününRejectReasonlistesinden seçilip açıklamaya yazılır. Bu dört alan popup düzenleme formunda yer almaz (akış yazar), listede kolon olarak görünür. Ayrıntı:lowcode.instructions.md§4.7. - Not yalnızca Table kaynağında → kayıt kaynağı (
SelectCommandType) Table olmayan listede (View, Query, StoredProcedure, TableValuedFunction) Not yoktur:PermissionJson.Nboş,ShowNotekapalı,{Kod}.Noteyetki satırı ve satırdaki Not butonu üretilmez; view/sorgu satırının notun bağlanacağı kalıcı kaydı yoktur. Wizard/MCP bunu kendisi uygular (ListFormWizardDto.HasNote), C# seeder'daDefaultPermissionJson(name, hasNote: false)yazılır. Ayrıntı:lowcode.instructions.md§4.2 (Üretilen yetkiler). - Özel komutun parametreleri boş bırakılmaz → MCP/prompt/wizard ile üretilen ya da düzenlenen ekranda yazılan her
InsertCommand/UpdateCommand/DeleteCommandve*BeforeCommand/*AfterCommandile aynı işteInsert/Update/DeleteFieldsDefaultValueJsonda tamamlanır: komuttaki her@Parametregönderilen alana,IsTenant'ın eklediği@TenantId'ye ya da listedeki bir satıra (Id=@ID,LastModifierId=@USERID,DeleterId=@USERID,CreatorId=@USERID,*Time=@NOW) bağlanır; kaynağı olmayan parametre "Must declare the scalar variable" verir. Wizard'ın audit varsayılanları view/sorgu kaynaklı ekranda ve özel komutta eksik kalır, bu yüzden kontrol üretendedir.SelectFieldsDefaultValueJsonçalışma anında okunmaz; okuma koşuluDefaultFilter'a yazılır. Ayrıntı:lowcode.instructions.md§4.3.5.3.
Cross-cutting: touching one thing means touching these too
-
Modül dokümanı →
ModuleDocManager.RefreshAllAsyncwizard'ı yazan her yoldan çağrılır (deploy, silme,WizardSeedSynchronizer, import kapanışı); wizard dosyasını yazan yeni bir yol eklenirse çağrı da eklenir, yoksa envanter bayatlar. Wizard → doküman/bölüm eşlemesi tek yerdedir (ModuleDocCatalog: dosya = kök menü, bölüm = beyan → alt formun ana ekranı → menü dalı; kök ve daldata/Sas.Menus.Menu.json'dan okunur, sabit liste yoktur); envanter biçimiModuleDocRenderer'dadır. Export kapanışı dokümanı taşır (WizardExportCollector), importdocs/modules/*.mdyolunu kabul eder. -
scriptRecipes.ts(TS) ↔Domain.Shared/Editors/*.cs— biri değişirse diğeri de değişir. İkisinin çıktısı birebir aynı olmak zorundadır; aksi hâlde seeder ile basılan script dialogda "elle düzenlenmiş" sayılır ve kural editörü kapanır. -
phoneFormat.ts↔DynamicReportPhoneFormatter.cs— telefon token'larının (phoneGlobal,phoneTR,phoneIntl,phoneCountry) görüntü kuralı iki yerde yazılır: liste/kart istemcide, dinamik rapor sunucuda. Biri değişirse diğeri de değişir; yoksa liste ile rapor aynı numarayı farklı yazar. -
Sequence.GetNextSequence(C#) ↔Adm_P_SequenceNext.sql(SQL Server + PG) — numaratörün biçim (FormatTemplatetoken'ları,PaddingSize) ve sıfırlama (ResetPeriod/LastPeriodKey) kuralı iki yerde yazılır: formdan açılan kayıt C#'tan, yordamın açtığı belge (tekliften sipariş, alternatif teklif) SQL'den numara alır. Biri değişirse diğeri de değişir; yoksa aynı numaratör iki farklı biçimde numara üretir. Eşzamanlılık korumaları ayrıdır ve ikisi de kalır: yordam satır kilidi (UPDLOCK/FOR UPDATE), C# karşılaştır-ve-yaz (ISequenceRepository.TryAdvanceAsync,SequenceManageryeniden dener) — entity izlenerek okunupUpdateAsyncile yazılmaz, iki form aynı numarayı alırdı. -
Liste popup'ı (Grid/Tree) ↔ form sayfası (
FormDevExpress) — editör yetenekleri ortak yardımcılardadır (list/shared/editingForm.ts,editingFormScripts.ts,cascade.ts); TagBox/GridBox/ImageUpload/ImageViewer/Rating'in tek uygulamasıform/editors/*, liste hücresi yalnızcalist/editors/cellEditors.tsxadaptörüdür. Bir tarafa yetenek eklenirken ortak yardımcıya eklenir; tek tarafa yazılan yetenek eşliği bozar. Tablo:lowcode.instructions.md§4.3.4. -
htmlEditorOptions.ts↔EditorOptions.Html— HtmlEditor varsayılanı iki yerde yazılır ve bloklar birebir aynıdır. İstemci iç içe blokları tek seviye birleştirdiği için seed edilentoolbarvarsayılanın yerine geçer; biri değişip diğeri değişmezse seed'li ekranlar eski araç çubuğunu gösterir. -
Wizard seed dosyası tek
Wizardbölümüdür → dosyadaListForm/Fieldsbölümü yoktur; ekranın bütün sözleşmesiWizardbölümündedir. ListForm detayının her sekmesi orada bir bloktur (Editing,Insert/Update/Delete,FormDefaults,Permission,FilterRow…PivotOption, görünüm option blokları,ExtraFilters,CommandColumns,SubForms,Widgets,WorkflowDto,ChartOptions), alan detayının her sekmesiGroups[].Items[]öğesinin bir alanıdır (WizardColumnItemInputDto:Filter,Header,Grouping,TotalSummary/GroupSummary,Join,ValidationRules,Styling,Permission,PivotSettings,ListOrderNo…). Wizard alanlarıMenu,Language,Data,Details,Layoutbaşlıklarına toplanır veListFormCode→WizardCode,MenuParentModuleId→ModuleId,LanguageTexts→Language.Textsadıyla yazılır; varsayılanı olan alanlar (CreateMenu: true, boşDataSourceConnectionString, boşMenuParentPath/LanguageTexts) hiç yazılmaz. Kısayol cevaplar (AllowAdding/AllowUpdating/AllowDeleting/AllowDetail/OpenDetailAfterInsert/ConfirmDelete,SeedData/SeedSync, öğedekiIsRequired) dosyaya yazılmaz:Editing,Data.SeedFilePath/SeedSync*veValidationRules'tan türetilir (SyncDerivedAnswers); deploy girdisinde yazılırsa bloğu ezer. Bu yalnızca dosya biçimidir; C# sözleşmesi düzdür, dönüşümWizardSeedFileDto.ToSeedJson/FromSeedJsoniçindedir. Wizard seed dosyası her zaman bu iki metotla okunup yazılır; düzJsonSerializerçağrısı başlıkları ve türetilen kısayolları sessizce bozar.FromSeedJsoneski biçiminListForm/Fieldsbölümlerini okuyupRefreshFromileWizard'a aktarır; bir sonraki yazımda düşerler. -
Wizard ile üretilen ekran → ListForm detayından yapılan her kayıt (
ListFormsAppService,ListFormFieldsAppService,ListFormJsonRowAppService) seed dosyasınınWizardbölümünüListFormWizardDto.RefreshFrom(listForm, fields)ile geri yazar (WizardSeedSynchronizer). Bu üçlüden birine yeni bir kayıt yolu eklenirse senkronizasyon çağrısı da eklenir; yoksa dosya bayatlar. Eşlik kuralı:GridOptionsEditDto/ColumnFormatEditDto'ya eklenen her alanınListFormWizardDto/WizardColumnItemInputDto'da karşılığı +RefreshFrom+BuildListForm/BuildFieldseşlemesi olmak zorundadır; yoksa tasarımcıda yapılan değişiklik migrate'te kaybolur. -
Wizard girdisinden ekran üretimi → tek hat
ListFormWizardDto.ApplyDefaults(isDeleted, isCreated, isModified)(boş bloklarıWizardDefaults+WizardConstsile doldurur; yetki adları, popup başlığı, durum anahtarı ekran kodundan yeniden türetilir) →BuildListForm()/BuildFields()(salt eşleme, parametresiz) →RefreshFrom(üretilen ekran cevaplara geri yazılır, dosya öyle kaydedilir); entity'ye yazmaGridOptionsEditDto.ApplyTo/ColumnFormatEditDto.ApplyTo. Deploy, seeder (WizardDataSeederdosyadanWizard.BuildListForm()/BuildFields()) ve senkronizasyon aynı metotları çağırır; ikinci bir üretim kopyası yazma. -
Katman sınırı (DDD) →
DbMigratorApplication'a referans vermez. Seeder ile AppService'in paylaştığı dönüşümApplication.Contracts'ta DTO metodudur; entity'ye yazma Domain.Shared'dekiIListFormArtifact/IListFormFieldArtifactsözleşmeleri üzerinden yapılır. Ayrıntı:dotnet.instructions.md§5. -
Yeni bir yetki →
PlatformConsts.AppCodes+data/Abp.Identity.Permissions.json+ (UI'da kullanılacaksa)permission.constant.ts+data/Sas.Languages.LanguageText.json. Yetki ekranındaki sıraAbpPermissions.Order(grup içinAbpPermissionGroups.Order) sütunundan gelir ve seed satırındakiOrderalanıyla yazılır; sıra kardeşler arasındadır — her üst yetkinin altında 1'den başlar, yeni satırı menüdeki yerine denk gelen numarayla ekle. -
/mcpucuna yeni bir araç → üç yer birlikte değişir:McpToolNames(ad kayıt defteri), ilgiliMcp*Toolssınıfı (Sozsoft.Mcp.HttpApi) ve arkasındaki AppService sözleşmesi (Sozsoft.Mcp.Application.Contracts). Arac adıMcpToolNames.Alliçinde yoksa araç listesi süzgecinden geçmez ve hiç görünmez; MCP Servers ekranındakiAllowedToolsseçenekleri deSelectableTools'dan üretildiği için kendiliğinden güncellenir. Sunucu kaydında tek anahtarAllowedTools'tur: boşsa tüm araçlar, doluysa yalnızca seçilenler açıktır; araç tipine göre ayrı bir bayrak yoktur, sınırı kullanıcı yetkisi çizer. İki MCP yetkisi vardır:.Invokeuca erişim ve okumadır (MCP servislerinin sınıf düzeyinde aranır);.WriteMCP üzerinden yapılan her değişikliktir ve değişiklik yapan her araçRequiredPermissions'ta.Write'a bağlanır — okuma/yazma ayrımı için yeni yetki açılmaz. Kiracı veri okur ve yazar, geliştirme yapamaz: kiracı bağlamında açık araçlarMcpToolNames.TenantToolsile sınırlıdır (şema,query_list,run_select,insert_row/update_row/delete_row) veAllowedToolsbu kümeyi yalnızca daraltır. Satır yazma ekranın C/U/D sözleşmesinden geçtiği için kiracıya açıktır; ekran yetkisinden geçmeden yazabilen araç (procedure, Custom Endpoint) ve geliştirme araçları (DevelopTools)TenantTools'a eklenmez. Geliştirme işlemi yine Wizard / Custom Components AppService'inden geçer./mcpistek düzeyindeki unit of work'ün dışındadır (AbpAspNetCoreUnitOfWorkOptions.IgnoredUrls): cevap SSE ile aktığı için istek UoW'si sonuç istemciye gittikten sonra commit edilir ve bağlantı kapanınca geri alınır — araç başarılı görünür, EF kayıtları kaybolur. Her MCP servis çağrısı kendi UoW'sini sonuç dönmeden tamamlar; bu istisna kaldırılmaz. Kapı iki yerdedir:AddListToolsFilterlisteyi süzer,McpAppService.EnsureToolAllowedAsyncçağrıyı reddeder — süzme tek başına yetki sınırı değildir, ikisi birlikte yazılır.Invokeuçta değil her MCP servisinin sınıf düzeyindeki[Authorize]'dadır; yeni bir MCP AppService'i de bu attribute'la açılır. MCP'nin kendi reddi istisna değildir, modelle konuşmadır: eksik/hatalı girdi, durum kuralı, kapalı araç ve eksik yetkiMcpProblemolarak döner — MCP AppService'leriMcpOutcome<T>/McpProblem?döndürür, domain servisleri (McpSqlGuard,McpTableCreator,McpSqlObjectWriter) de öyle; araç katmanı bunuMcpToolResult.Fromile hata sonucuna (CallToolResult.IsError) çevirir, model mesajı okuyup tekrar dener.throw new UserFriendlyExceptionveAuthorizationService.CheckAsyncMCP kodunda kullanılmaz (debugger'ı durdurur, SDK'da yığın izliERRüretir); yetkiMcpAppService.CheckPermissionAsyncile adıyla istenir. İstisna yalnızca gerçek hatadır (kod hatası, beklenmeyen durum). Arkadaki platform servisinin (wizard, custom component, ListForm verisi) attığıUserFriendlyException/yetki reddi ile sınıf düzeyindeki[Authorize]reddiMcpToolErrorTranslator(çağrı filtresi) ile mesaja çevrilir. Yeni aracın arkasındaki servis bir yetkiyi[Authorize]attribute'uyla istiyorsa (ret mesajı yetki adını taşımaz) o yetkiMcpToolNames.ServicePermissions'a eklenir; ekran yetkisi gereken araç yetkiyiIMcpListFormAccessFilter.GetPermissionNameAsync+CheckPermissionAsyncile adıyla önceden ister. Araç → yetki tablosuREADME.md§13.9'dadır; araç ya da yetki değişirse tablo da değişir. Hazır prompt'lar (McpPromptNames+McpDevelopPrompts) yalnızca metindir; adımları tekrar etmez,get_dev_guidebölümüne yönlendirir. Bir kural bağlayıcıysa prompt'a değil araca yazılır (örnek:Ready to Startşartıstart_dev_request'tedir). -
Wizard deploy veritabanına yazmaz → wizard ekranı, MCP
deploy_wizardve düzenleme aynı yoldan (ListFormWizardAppService.Create) geçer ve yalnızca seed dosyası yazar:wizard/{Ad}.json- menü/modül/izin grubu/yetki/dil satırları (
data/Sas.Menus.{Menu,Module}.json,data/Abp.Identity.Permission{Groups,s}.json,data/Sas.Languages.LanguageText.json;WizardSeedRowStore, anahtarlarWizardSeedRowKeys). Kurulum yalnızca DB Migrate'tedir:WizardDataSeederekrana özgü kayıtları (veri kaynağı, ListForm, alan, iş akışı) kurar,ListFormSeedDataAppliersatırları ekler. Deploy/import dosyayaPendingyazar; Wizard Manager onu "Migrate bekliyor" gösterir (WizardFileInfoDto.IsPending), migrate önceki sürümün kayıtlarını (Pending.Replaced) kaldırıp ekranı yeniden kurar vePending'i siler. Yetkiler hiçbir role otomatik atanmaz (admin dahil); rol ekranından verilir. Deploy'a veritabanı yazan ya da silen bir adım ekleme. Ayrıntı:lowcode.instructions.md§4.0.
- menü/modül/izin grubu/yetki/dil satırları (
-
MCP ile demo veri → geliştirme isteği örnek satır taşıyorsa (prompt'taki tablo/liste, ekran görüntüsü) ekran
deploy_wizard'aSeedData: trueile gider (SeedFilePath = data/{ListFormCode}.json;SeedSync*bayrakları kapalı kalır — yalnızca istek ekrandan girilen kayıtların dosyaya yansımasını açıkça isterseSeedSync: true, wizard ekranında da aynı;ListFormWizardDto.BuildListForm) ve satırlar aynı çağrıdaseedRowsileconfigs/seeds/data/{ListFormCode}.jsondosyasına yazılır (McpDevelopAppService; alan adları ekrana göre doğrulanır,Guidanahtarı sunucu üretir). Migrate ekranı kurar, ardından satırları ekler; MCP migrate'i beklemez. Talep döngüsüTesting'de biter,Staging'e kullanıcı alır. Dosya elle yazılmaz, görselden okunamayan değer uydurulmaz.SeedDatawizard ekranında sorulmaz ama düzenlemede korunur (RefreshFrom+ UI modeli). Ayrıntı:lowcode.instructions.md§0.10.1. -
Talepteki görsel →
Sas.DevRequestsaçıklamasına yapıştırılan görsel base64data:URI olarak kalır, hiçbir yere yüklenmez.start_dev_request/get_dev_requestonu metinden ayıklar (McpDevRequestImages) ve MCP resim içeriği (ImageContentBlock) olarak döndürür; metinde[image-N]kalır. Sonucun tamamı istemcinin araç sonucu sınırına (Claude Desktop 1 MB) sığar: kalan pay görsellere bölünür, payını aşan görselIMcpImageShrinker(Host'ta SkiaSharp) ile küçültülüp JPEG'e çevrilir, yine sığmayan gönderilmez ve metinde bildirilir; kaynak açıklama değişmez. Açıklama bu yüzdenupdate_rowile yeniden yazılmaz.SubTasksdeadd_dev_request_subtasksile yazılır: atanan kişi bağlanan kullanıcı, bitiş tarihi bugün — sunucu doldurur, modele bırakılmaz.Timesheets'i tamamen sunucu yazar (IMcpDevRequestTimesheetRecorder):get_dev_request/start_dev_requesttalebi etkin talep yapar, sonraki her üretim/veri yazma işlemi (create_table,save_sql_object,generate_crud_endpoints,deploy_wizard,delete_wizard,save_component,delete_component,save_report_category,save_report_template,delete_report_template, satır araçları,save_dev_request_analysis,append_dev_request_note,add_dev_request_subtasks) onaSas.Mcp.Timesheet.*anahtarından Türkçe açıklama ve ölçülen süreyle bir satır ekler; yalnızca talebi açanstart_dev_requestsatır yazmaz. Yeni bir üretim/veri yazma aracı eklenirse kaydedici çağrısı ve dil anahtarı da eklenir. Satır aracın UoW'si commit edildikten sonra ayrı UoW'de yazılır; aynı UoW'de yazmak Dapper transaction'ınınSas_H_DevRequestkilidini EF'in ayrı bağlantısından bekletip zaman aşımına düşürür. -
Talep analizi →
Descriptiontalebin yazıldığı hâliyle kalır; MCP ona yazmaz. Analiz ve üretim notlarıAnalysiskolonundadır (Todo panosundaAnalysisExpr, Açıklama yanında sekme). Durum sırasıBacklog → Analysis → Ready to Start → …: "Analiz et"save_dev_request_analysisile yalnızca üretimi başlamamış (Backlog/Analysis/Ready to Start) talebi analiz eder (ekran/tablo üretmez, modül dokümanına yazmaz — dosya ve dal bölümü üretimde, deploy ile açılır; onaylanan analiz bölümün "2. Analiz ve kararlar" alt bölümüne üretimdesave_module_docile yazılır). Durumu sunucuproductionReadyile belirler: "Üretime hazır: Evet" →Ready to Start, şartlı ya da Hayır →Analysis(kullanıcı kararı bekler). Üretim notuappend_dev_request_noteileAnalysis'e eklenir. Analiz standardı tek yerdedir:lowcode.instructions.md§0.12; prompt'lar oraya yönlendirir. -
MCP şema araçları → tablo/sütun bilgisi canlı katalogdan değil
Sas_H_DbTableColumnsözlüğünden okunur (IMcpSchemaDictionary). Sözlüğün alan adlarıdbo.Sas_H_ListFormFieldsDictionaryJsonprosedürünün ürettiği JSON ile birebir aynıdır; prosedür değişirseMcpDictionaryListForm/McpDictionaryFieldde değişir. Sözlük tablo kataloğunu değil ekranların alanlarını döker: bir tablonun ekranda alan olarak tanımlanmamış kolonunu MCP göremez. Modelin okuması gereken kolon (ör.AbpUsers'ın kişisel bilgileri) ilgili ekranda gizli alan olarak durur. -
MCP OAuth → bilinen sağlayıcılar (Claude, ChatGPT) seed edilmiş
Platform_Mcpistemcisini, yerelmcp-remoteproxy'si açık onaylıPlatform_Mcp_Localistemcisini, diğer MCP istemcileri/connect/registerile üretilenmcp_önekli açık onaylı istemcileri kullanır (McpOAuthClientManager; seedMcpOAuthClientDataSeedContributor). Bilinen sağlayıcı eklemek = DbMigrator'dakiPlatform_Mcp:RedirectUrislistesine callback eklemek. MCP istemcisi kararı tek yerdedir (McpConsts.IsMcpClient):resourceparametresi yok sayılır ve token kullanıcı oturumuna bağlanmaz; token ucuna yeni bir oturum yolu eklenirse bu istisna korunur, yoksa connector web oturumunu düşürür. -
MCP yalnızca seed dosyası yazar → Wizard gibi: geliştirme araçları (
create_table,save_sql_object,generate_crud_endpoints,deploy_wizard) yalnızcaconfigs/seeds/altına dosya yazar, veritabanına DB Migrate yansıtır. Yeni bir geliştirme aracı veritabanına yazan AppService'i çağırmaz, seed dosyasını üreten ortak metodu kullanır (ör.CrudEndpointSeedFileDto.Generate/Write; diyaloğun servisi de aynı metottan geçer). Hata kodu yalnızca migrate'i bozacak girdi içindir; düzeltilebilen girdi (bilinmeyen seçenek, boş/tekrar eden index) sessizce normalize edilir. Kolon adları tahmin edilmez: şema araçları geliştiriciye migrate bekleyen wizard ekranlarını ve seed-only tabloları da döner (McpSchemaAppService.GetSeedOnlySourcesAsync).deploy_wizardgirdisi dosya biçimi ({"Wizard": {...}}, başlıklı) ya da düz cevaptır (WizardSeedFileDto.FromDeployJson); bloklar isteğe bağlıdır (yazılmayan blokApplyDefaultsile dolar), kısayol cevap bloktan üstündür, eski biçiminListForm/Fieldsbölümleri reddedilir (Sas.ListForm.WizardError.LegacySectionsNotAccepted);get_wizardyeni biçimi döner. -
MCP yetki vermez → yeni bir MCP aracı kullanıcının uygulamadaki yetkisini aşamaz: liste verisi ekranın sorgusundan (
IListFormSelectAppService/IListFormDataAppService) geçer; liste yetkisini aşan bir yetenek (ham SQL gibi)McpToolNames.RequiredPermissions'a uygulamadaki karşılık gelen yetkiyle eklenir.create_tablebu yüzden.Write'a ek olarakSas.SqlQueryManagerister; tablo scriptini tasarımcınıngenerateCreateTableSqlçıktısıyla aynı biçimde üretir (McpTableCreator) ve veritabanında çalıştırmaz — MCP veritabanı kurmaz, tabloyu da ekran gibi kullanıcının çalıştırdığı DB Migrate kurar; wizard deploy'u kurulmamış tablonun kolonlarını seed scriptinden okur (PostgreSqlTableScript.ReadSeedColumns) — tasarımcının şablon kolonları ya da script biçimi değişirse o da değişir. Tasarımcı (SaveTableScriptAsync) vecreate_tableT-SQL dosyanın PostgreSQL eşiniPostgreSqlTableScriptile üretippostgres/object/altına yazar; script biçimine yeni bir deyim eklenirse çevirisi de eklenir, yoksa PG eşi yazılmaz (log uyarısı). Düşürülen nesnenin dosyası iki klasörden de silinir. -
Çalışan (persona) ekranları ve profil → çalışanın/yöneticinin kendi listeleri İK menüsüne konmaz: menüsüz
Hr.My*/Team*wizard + Intranet panosu widget'ı. Profil sayfasının İzinlerim / Puantajım / Performansım sekmeleri persona ekranlarını salt okunur gösteren çekirdek bileşenlerdir (düzenle/sil yok, tek aksiyon izin talebi oluşturma); yeni persona ekranını profile eklemek kod + rota değişikliğidir (onay). Çalışan Bilgileri sekmesi ve Genel sekmesi kullanıcının kendi alanlarınımy-profileek alanlarından okur (PlatformProfileAppService.ProfileExtraProperties); sağlık, ücret ve işten çıkış alanları bu listeye girmez. Ayrıntı:lowcode.instructions.md§0.12.1. -
Bildirim kapsamı → bildirim tipi, kuralı ve kuyruğu kiracıya aittir (
Adm_T_NotificationTemplate,Adm_T_NotificationType,Adm_T_NotificationRule,Adm_T_Notification; dördü deIMultiTenant) ve ekranların tamamı Administration › Bildirimler altındadır. Ayrı bir manuel gönderim tablosu yoktur: Bildirimler ekranının ekleme formu elle gönderimdir, uç alıcı × kanal açılımını kuyruğa yazar (list-form-dynamic-api/notification-insert). Yeni kiracıya varsayılan tür/kural kümesi gelir: kendi veritabanı olan kiracı seed dosyasından alır, host veritabanını paylaşanaNotificationTenantDefaultsSeedContributoraynı dosyalardan yeni kimliklerle kopyalar; iki yol aynı kiracıda çalışmaz. Kiracı verisine dokunan zamanlanmış işinScopealanıTenantya daHost ve Kiracılarolmalıdır —Hostkapsamında çalışan bir iş kiracı satırlarını göremez (WorkerScopeEnum,PlatformBackgroundWorker). -
Bildirim bayrakları → sıra değişmez:
IsSent(iletildi) olmadanIsReadyazılmaz,IsReadileReadTimebirlikte yazılır.UiActivitykayıt oluşurken iletilmiş sayılır,UiToast/Desktoptarayıcıda gösterildiğinde (NotificationChannels.BrowserChannels;sent-manyucu yalnızca bu kümeyi işaretler), kalan kanallarıNotificationWorkerişaretler — gönderim okuma değildir. Kullanıcı tercihi (IsCustomized+RecipientType = User) kural değildir, yalnızca sahibini kapatır; alıcıyı kurallar belirler. Yeni bildirim üretildiği anda/notificationhubile duyurulur (INotificationRealtimeNotifier; sözleşme bildirim modülünün Domain'inde, SignalR gerçeklemesi barındıran uygulamada — modül katmanı ASP.NET Core'a bağlanmaz) — duyuru en iyi çabadır, istemcideki yedek yoklama kaldırılmaz. Ayrıntı:README.md§13.3–§13.5. -
SEO → ayrı bir SEO tablosu yoktur; meta, yolu tanımlayan kaydın üzerinde yaşar. Üç parça birlikte çalışır: genel varsayılanlar
Adm.SiteManagement.Seo.*ayarları, yol bazlı alanlarRoute.Seo*veCustomComponent.Seo*(ikisinin deSeedSync*bayrakları açık), istemci tarafıSeoHead. Üçü de uygulama konfigürasyonununseoalanından beslenir (PlatformApplicationConfigurationContributor→SeoAppService.GetConfigAsync);sitemap.xmlverobots.txtaynı servistenSeoControllerile yayınlanır. Aynı yolu iki kaynak da tanımlıyorsa bileşen kazanır — çalışma zamanında da bileşen fiziksel rotayı gölgeler. Ziyaretçiye açık yeni bir yol üreten her iş SEO alanlarını da doldurur (standing default 7). Sitemap'e giren kaynaklar (Pubmenüleri, aktif custom component rotaları, yayınlanmış blog yazıları)SeoAppServiceiçindedir; yeni bir public kaynak türü eklenirse toplayıcıya da eklenir, yoksa sayfa sitemap'te görünmez. Yeni bir meta alanı = iki entity +SeoRouteDto+SeoFields(seeder) +SeoHeadbirlikte. -
Yeni bir dil anahtarı →
configs/seeds/data/Sas.Languages.LanguageText.json(EN + TR iki satır;Id=Sas_H_LanguageText:{ResourceName}:{Key}:{CultureName}adından URL isim alanlı v5 GUID); eklemeden önce anahtarın zaten var olup olmadığını kontrol et. -
Custom component sınıfları → bileşen kodu veritabanında yaşar, Tailwind derlemede yalnızca seed dosyasını görür. Seed dosyası
ui/dışında olduğu için aynı sınıflarui/safelist.txtdosyasına da yazılır (ui/twSafelistGenerator.tsVite eklentisi, derlemede otomatik); yoksa uygulama sunucuda stilsiz kalır. İkinci bir safelist dosyası açma. Çalışma anında veriden üretilen sınıf (bg-${renk}-500) dosyaya değilsrc/assets/styles/tailwind/index.css'teki@source inlinesatırına eklenir. Sınıf adları Tailwind 4'tür (shadow-xs,rounded-sm,bg-black/50,shrink-0,x!); tematailwind.config.ts'te değil aynı dosyanın@themebloğundadır. -
Custom component kaydı/silinmesi →
data/Sas.DeveloperKit.CustomComponents/{Id}.json(Custom Components ekranının seed verisi); ekran klasör modundadır, her bileşen kendi satır dosyasındadır. Hem listeden hem Visual Designer'dan (CustomComponentAppService) yapılan kayıt aynı klasöre yansır; kullandığı endpoint'lercrud/{Entity}.json. -
Wizard dosyası → tek başına taşınmaz; export zip'i özyinelemeli bağımlılık kapanışını taşır (
WizardExportCollector): alt formların wizard dosyaları, ekranınSeedFilePathveri aynası, lookup sorgularının işaret ettiği nesnelerin scriptleri,crud/ve{sql|postgres}/{object|execute}/dosyaları.Customyolundadata/girdisi yalnızca ilgili bileşen ve bağımlılık zincirine süzülür; import'ta hedef dosyanın üstüne yazılmaz, satırlar anahtar üzerinden birleştirilir. Kapanışa yeni bir bağımlılık türü eklenirseWizardImportValidator'daki karşılığı da eklenir — biri toplar, diğeri eksikliğini yakalar. -
Talep bağı (
DevRequest) → MCPdeploy_wizardwizard dosyasına etkin talebi (IMcpDevRequestTimesheetRecorder.GetActiveRequestIdAsync)DevRequest { Id, Title }olarak yazar; bağ modelin girdisinden gelmez (ListFormWizardDto.DevRequestId[JsonIgnore]). Elle yapılan kayıt önceki bağı korur, talepsiz wizard'da bölüm hiç yazılmaz. Wizard Manager talebi gösterir, kaydına bağlar ve talebin bütün wizard'larını tek kapanışta export eder (export-dev-request;WizardExportCollector.CollectAsyncbirden çok dosya alır). Wizard dosyasını yeniden yazan yeni bir yol eklenirseDevRequestkorunur — dosyayı DTO olarak okuyup yazan yollar bunu kendiliğinden yapar. -
Şema değişikliği → ayrım kayıp riskine göredir. Eksik kolon eklenir (kayıpsız;
ALTER TABLE ADD), var olan kolon hiçbir zaman değiştirilmez, hiçbir kolon düşürülmez, hiçbir tabloDROPedilmez. Ölçü farkında (uzunluk/kesinlik/nullability) import durdurulur ve fark somut olarak bildirilir; varsayılanı olmayanNOT NULLbir kolonun dolu tabloya eklenmesi de durdurulur — hangi değerin yazılacağı iş kararıdır. Karşılaştırma tek yerdedir (SqlTableSchemaComparer), analiz ve kapanış aynı sonucu kullanır. Karşılaştırmada tip adı kullanılmaz — sağlayıcı katalogları farklı kanonik ad raporlar ve metin karşılaştırması sahte fark üretir. Genel kural: sağlayıcıya göre değişen bir değeri metin olarak karşılaştırma, katalogdan dinamik oku. -
Veritabanına dokunan her import ucu →
EnsureImportNotBlockedile başlar. Analizdeki engelleme kararı sunucudablockeddosyasında durur; yeni bir uç eklenirse kontrolü de eklenir, yoksa karar istemci tarafından atlanabilir hâle gelir. -
Wizard import'u → analiz dosyalar yazılmadan önce doğrular (
WizardImportValidator);Errorseviyesinde bir bulgu varsa oturumablockedişareti düşer veApplyImportreddeder. Kapanış (CompleteImport) bağımlılık sırasıyla çalışır:sql/object→sql/execute→crud→data→wizard. Tablo eksikse önce scripti çalışır; sıra değiştirilmez. Ekran import'ta kurulmaz: wizard dosyasınaPendingyazılır ve ekran deploy'daki gibi DB Migrate'te kurulur; dosyanınWizardbölümündeki bloklar olduğu gibi uygulanır (BuildListForm/BuildFields), varsayılanlar yeniden üretilmez — tasarımcı değişiklikleri bloklarda durduğu için korunur. Wizard'ın menü/yetki/dil satırlarını taşıyan platform dosyalarıdataadımında uygulanmaz (WizardSeedRowStore.ListFormCodes). Engelleyici bulgu iki sınıftır: arşiv kaynaklı (RequiresReexport— bozuk/eksik dosya, karşılığı olmayan referans) hedef ortamda giderilemez, export tekrarlanır; ortam kaynaklı (eksik tablo, şema farkı) ekrandan giderilir. Yeni birErroreklerken hangisi olduğu işaretlenir. Engelleme oturum genelidir, dosya bazlı değildir — import ya bütün olarak çalışır ya da hiç. Bunun bedeli, her engelin ekran içinde çözülebilir olmasıdır: tabloya dair bir bulgu üretirkenTableName(+ tablo hedefte yoksaTableMissing) doldurulur, istemci tablo tasarımcısını buna göre açar. -
ListForm.SeedFilePathdolu bir ekran →SeedSyncInsert/SeedSyncUpdate/SeedSyncDeletebayraklarından işaretli olan işlemler seed klasöründeki hedefe yansır (ListFormSeedDataSynchronizer); yalnızca host bağlamındaki kayıt yansır, kiracının kaydı veritabanında kalır. Hedef iki biçimdedir ve ayrım uzantıdadır:.jsonile biten değer tek dosyadır (data/{ListFormCode}.json, bütün satırlar orada), uzantısız değer klasördür (data/{ListFormCode}/{anahtar}.json, her satır kendi dosyasında) ve klasör yoksa ilk kayıtta oluşturulur. Klasör modu satırı büyük ya da sık yazılan ekranlar içindir (varsayılan tek dosya). Biçimin tek kaynağı ekranın tanımıdır; diskte ne bulunduğu kararı değiştirmez ve tanım kod içinde ekran adıyla sabitlenmez. Hedefi okuyan/yazan her yol (senkronizasyon, export/import, temizleme, wizard satır deposu, MCP demo satırları, bildirim varsayılanları)ListFormSeedTargetüzerinden gider; paylaşılan ekranların tanımıListFormWizardAppService.ReadSeedFilePathsAsyncile okunup bileşenlere verilir. Applierdata/altını özyinelemeli tarar ve bir klasörün tamamını tek unit of work'te uygular — ekran tanımı, alan listesi ve veri kaynağı satır dosyası başına değil ekran başına bir kez çözülür; dosyadakiListFormCodeboşsa ekran kodu klasör adından gelir. Ekran bir hedef tanımlıyorsa yalnızca o hedef uygulanır; tanımın dışında kalan dosya (biçimi değişmiş ekranın eski dosyası) uyarıyla atlanır. Biçimi değiştirmek tanımı (ListForm detayı ya da seeder) değiştirmektir; yeniden wizard deploy'u tanımlı hedefi korur. Migrate/seed sırasında ve wizard import'unun kapanışında (CompleteImport)ListFormSeedDataApplierdosyayı geri uygular. Migrate/seed'de kayıt yoksa eklenir, varsa dokunulmaz; import'taoverwriteExistingaçıktır: var olan kayıt dosyadaki değerlerle güncellenir ve soft delete izleri (IsDeleted/DeletionTime/DeleterId) temizlenir. Satırı veritabanından okuyan senkronizasyon açık UoW varsa commit sonrasında kendi UoW'sinde çalışır (EF ile yazan çağıran, ör.MenuAppService, aksi hâlde eski değeri dosyaya yazardı); yalnızca dosyaya dokunanRemoveRowsByFieldAsynchemen çalışır. Yeni bir kayıt yolu (yeni endpoint, toplu işlem, tipli AppService) eklenirse senkronizasyon çağrısı da eklenir; yoksa dosya bayatlar. -
Seed kapsamı →
configs/seeds/tek klasördür;host/ya datenants/{tenantId}/yoktur. Host ve kendi veritabanı olan her tenant aynı dosyalarla kurulur, host veritabanını paylaşan tenant'ta dosyalar yeniden uygulanmaz (SeedFileScope; migrate kararıDataSeedContext'e yazar). Dosyayı okuyan yeni bir seederSeedFileScope.IsEnabledile başlar. Dosyalar yalnızca host bağlamında yazılır; dosyaya yazan yeni bir yol tenant bağlamında yazmaz. Bekleyen wizard kurulumu (Pending) dosyaları uygulayan son veritabanında kapanır (SeedFileScope.CompletesPending). Tek istisna{sql|postgres}/host/{object,execute}: yalnızca host'ta çalışır; tenant veritabanında karşılığı olmayan nesneye (HangFire tabloları) dayanan script buraya konur, tablo önekine bakılmaz. Kendi veritabanı olan tenant'ta EF ile yalnızcaIMultiTenantvarlıklar o veritabanına gider; ListForm, menü ve yetki tanımını ABP her zaman host'ta okur. Kiracı tablosunun satırları kiracı adıyla kayıtlıDataSourceüzerinden gider. Kiracı yalnızca kimliğiyle değiştirildiğinde ad boş kalır; bu durumda adıDataSourceManagerkiracı kaydından okur. Yeni tenant yöneticisi ilk seed'de kiracı tarafı bütün yetkileri alır (PlatformIdentityDataSeeder). Bunun nedeni, DbMigrator'da dinamik yetki deposunun kapalı olmasıdır: ABP'nin yetki seed'i seed dosyasından gelen yetkileri göremez. -
Seed dosyası yazmak → veri dosyası
ListFormSeedDataFileDto.Serialize, wizard dosyasıWizardSeedFileDto.ToSeedJsonile yazılır; düzJsonSerializer.Serialize+File.WriteAllTextkullanılmaz. System.Text.Json emojiyi her zaman vekil çift kaçışına çevirir; dosya baştan yazıldığında bütün emojiler sahte fark üretir. İkisi deSeedJsonText.RestoreSurrogatePairs'ten geçer. Veri dosyasınınGeneratedAtdeğeri oluşturma anıdır:Serializeyalnızca boşsa doldurur, hiçbir yazma yolu onu güncellemez — ekleyip geri kaldıran işlem (wizard deploy + silme) dosyayı birebir önceki hâline döndürür.crud/dosyalarının damgası ayrıdır ve karar verir (aşağıda). -
crud/{Entity}.json→ uygulama kuralıCrudEndpointSeedApplier'dadır ve damga karşılaştırmalıdır: eksik endpoint eklenir, mevcut kayıt yalnızca dosyanınGeneratedAtdeğeri kaydınLastModificationTime/CreationTimedeğerinden yeniyse güncellenir/silinir. Migrate kullanıcının runtime revizyonunu ezmez. Yeni bir seeder yazarken bu kuralı taklit et: migrate varsayılanı eklemektir, ezmek değil. -
Modül = menü = tablo öneki = yetki kökü → platformun C# seeder'lı kök menüleri
Sas,Adm,Hr(İK) vePub(kök menüPub.Header) modüllerindedir (TableNameResolver.MenuPrefixyalnızca bunları bilir); wizard ile üretilen modüller kendi kök menüsü ve tablo önekiyle gelir —Prj,Prc,Stk(Stk_),Mnt(Mnt_),Prd(Prd_); tam listedata/Sas.Menus.Menu.jsonkök satırlarıdır. Bir ekranı modüller arası taşımak birlikte yapılır:TableNameResolver(MenuPrefix) +AppCodes(Seed/Platform) +ListFormSeeder_{Sas,Adm,Hr}.cs+data/{ListFormCode}.jsonadı + menü/yetki/dil satırları + routeAuthority. YetkiModulesdeğeri: Sas/AdmSas|Adm, İKSas|Hr(Sashost,Adm/Hrkiracı görünürlüğü). İK departman/pozisyonBranchIdzorunludur; seed satırı host şubesine (PlatformConsts.Branches.BranchIdDefaultValue) bağlanır. -
Menü ağacı ve yetkiler →
configs/seeds/data/Sas.Menus.{Routes,Module,Menu}.jsonveAbp.Identity.Permission{Groups,s}.json; platformun kendi menü/yetki kayıtları da diğer liste verisiyle aynı mekanizmadan (ListFormSeedDataApplier) geçer, ayrı bir seeder yoktur.Orderalanı bağımlılık sırasını kurar: route → modül → menü, yetki grubu → yetki, ülke grubu → ülke → şehir → ilçe (Sas.Definitions.{CountryGroup,Country,City,District}.json), departman → onaycı (Hr.Department→Adm.Definitions.Approver), bildirim tipi → kuralı (Adm.Notifications.Notification{Types,Rules}.json). Para birimi, hitap, AI bot (Sas.Definitions.{Currency,ContactTitle,AiBot}.json), ayar tanımları (Sas.SettingDefinitions.json) ve zamanlanmış işler (Sas.BackgroundWorkers.RecurringJobs.json), dil → metin (Sas.Languages.{Language,LanguageText}.json;LanguagesData.json+LanguageDataSeederkaldırıldı) da buradadır;HostData.jsonkaldırıldı,HostDataSeederyalnızca bağlantı cümlesi appsettings'ten gelen varsayılan veri kaynağını kurar. Dosyalar ekranın anahtarını (KeyFieldName) kullanır;Guidanahtarlı satırınId'si iş anahtarından türetilmiş sabit bir GUID'dir, satır eklerken aynı kuralla üret.Sas.Menus.Routes.jsondosyasına yalnızca var olan satırınSeo*alanları için dokunulur (standing default 7); rota eklemek, silmek ya da yolunu değiştirmek çerçeve dışıdır. -
Public site → menü
MenutablosundaModuleId = "Pub"altında (PlatformConsts.PublicSite);Pub.Headerhem üst menüyü hem alt bilgideki hızlı bağlantıları besler (ikisi dePublicNavdüğümü) ve ziyaretçiye uygulama konfigürasyonununpublicMenusalanıyla taşınır (menü ucu[Authorize]). Sayfa ise Custom Component'tir:/admin/ile başlamayanRoutePathotomatik public route olur ve aynı yolu paylaşan fiziksel route'u gölgeler;RoutePathboş bırakılabilir (rotasız bileşen). Yeni public sayfa =data/Sas.DeveloperKit.CustomComponents/{Id}.jsonsatırı +Pubmenü kaydı; ikisi birlikte yazılır. Üst ve alt bilgi rotasız bileşenlerdir (PublicHeader,PublicFooter), layout ikisini derenderComponentile çizer; rotasız bileşendeRoutePathNULL yazılır (tekil indeks yalnızca dolu değerleri kapsar). Üst bilginin davranışıcomponents/publicSitekatalog düğümlerinde, yerleşimi tasarımcıdadır. Public bileşenlerin hepsi tasarımcı belgesidir (Props.visualDesigner,sourceMode: visual); kod belgeden üretilir (generateDesignerCode), elle yazılmış koda dönülmez. Runtime kapsamındareact-icons/react-routeryoktur — ikonPlatformIcondüğümü, gezinmeadüğümü. Kanvasın doğrudan çizdiği katalog düğümleri tek sözlüktedir (componentEditor/runtimeNodes.ts); yeni bir düğüm eklerken bileşen + bu sözlük +catalog.tstanımı + dil anahtarı birlikte yazılır. Elle yazılmış bir bileşen tasarımcıda kod modunda açılır, kanvasa çevrilmez. Ayrıntı:README.md§10.4. -
Çok değerli alan (
dxTagBox/ çokludxGridBox) → tek metin kolonunda|ile saklanır. Ekran varsayılanlist-form-data/*ucundan kaydediyorsa birleştirmeyiQueryHelperyapar; tipli DTO alan bir uca (list-form-dynamic-api/..., Custom Endpoint, Dynamic Service) bağlıysa DTO alanına[JsonConverter(typeof(MultiValueStringJsonConverter))]eklenir — yoksa istek400ile düşer. Ayrıntı:lowcode.instructions.md§4.3.2.1.
Definition of done
- Ölü kod bırakma: kullanılmayan
using, private üye, alan, DTO, hook, servis, tip. Bir tip silinmeden önce repo geneli (api + ui + configs) referans taraması yapılır; DI ile çözülen tipler düz metin aramasında referanssız görünür. - Yorum satırına alınmış kod bloğu bırakılmaz.
- Seed tarafında ölü artefakt yok (
lowcode.instructions.md§8.6): yerini yenisine bırakan SQL nesnesi, wizard ve veri dosyası silinmiş; hersql/dosyasınınpostgres/eşi var; kullanılmayan dil anahtarı wizard'ınLanguage.Texts/InsertedRecords'undan ve dil dosyasından çıkarılmış. - Modül/ekran/bileşen ekleyen ya da davranışını değiştiren iş modül dokümanını
(
configs/seeds/docs/modules/{Menü}.md) yazar veya günceller; liste ekranının açıklaması ayrıntılıdır (standing default 14–15). api/değişikliğinden sonra:dotnet build -p:EnforceCodeStyleInBuild=true— 0 hata, yeniIDE00xxyok.ui/değişikliğinden sonra:npm run typecheckvenpm run lint.- Yorumlar nedeni anlatır, ne yaptığını değil. Kod zaten ne yaptığını söylüyor.
- Dokümantasyon kodla birlikte güncellenir. Platformun davranışını değiştiren her değişiklik
(yeni/kaldırılan seeder, seed klasörü ya da dosya biçimi, ListForm sözleşmesine alan ekleme,
yeni yetki/dil anahtarı, katman sınırı, yeni mekanizma) aynı commit içinde ilgili
.mddosyasına yansıtılır:README.md(ne nerede yaşıyor, klasör düzeni, tablolar),.github/instructions/lowcode*.instructions.md(artefakt/yetenek sözleşmesi),dotnet.instructions.md(kod standardı, katman kuralı),CLAUDE.md(çalışma kuralı). Değişiklik hangi md'de karşılığı olduğu bilinmiyorsa önce grep ile eski anlatım aranır ve düzeltilir; bayat kalan bir satır bırakma. Doküman güncellemesi "sonraya" bırakılmaz.
Tone
- Türkçe soruya Türkçe cevap.
- Platformu tanıtma, mimariyi özetleme, ne yapacağını anlatıp sonra bir de ne yaptığını özetleme.
- Cevabın boyutu sorunun boyutu kadar olsun.