481 lines
38 KiB
Markdown
481 lines
38 KiB
Markdown
# 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)
|
||
|
||
1. **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.
|
||
2. **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.
|
||
3. **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.
|
||
4. **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.
|
||
5. **Respond in the language the user writes in** (Turkish → Turkish).
|
||
6. 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.
|
||
|
||
1. Dynamic configuration with existing ListForm ecosystem
|
||
2. SQL Query Manager + Custom Endpoint
|
||
3. Dynamic Service
|
||
4. Code change (last resort, justification required)
|
||
|
||
## Non-Negotiable Rules
|
||
|
||
1. Do not propose new custom React component/page development for standard feature requests.
|
||
2. Build new screens using platform configuration mechanisms.
|
||
3. Every implementation proposal must include tenant and permission design.
|
||
4. Never bypass platform authorization patterns.
|
||
5. Never hardcode secrets, tenant IDs, or connection strings.
|
||
6. Never step outside the framework without explicit approval (standing default 11).
|
||
|
||
Exception:
|
||
|
||
- Custom React/backend code is allowed only when the user explicitly requests implementation
|
||
and configuration is insufficient.
|
||
- In such cases, explain why configuration-first options are 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.
|
||
|
||
1. Goal
|
||
2. Decision flow result (which step used)
|
||
3. Artifacts to configure
|
||
4. SQL/query/endpoint design (if needed)
|
||
5. Menu + route + component mapping
|
||
6. Permission and role mapping
|
||
7. Tenant isolation notes
|
||
8. Validation and test checklist
|
||
9. Rollback strategy
|
||
|
||
---
|
||
|
||
## 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` |
|
||
| Yetki kodları | `PlatformConsts.AppCodes` (backend) + `constants/permission.constant.ts` (UI) |
|
||
| Seed yolları | `SeedPathResolver` |
|
||
|
||
Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` → bu dosya → `README.md`.
|
||
|
||
### Standing defaults — do not ask again
|
||
|
||
1. **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.
|
||
Yeni **tanım** tablosu (tanım olup olmadığına talebin içeriğine göre analizde karar verilir;
|
||
sabit ad listesi yoktur) buna ek olarak `IsActive bit NOT NULL DEFAULT 1` ile gelir; liste + form alanı, varsayılan
|
||
`true`, Aktif/Pasif widget'ı. Ayrıntı: `lowcode.instructions.md` §8.1.
|
||
Görsel tutan her kolon (fotoğraf, logo, avatar kopyası) `nvarchar(MAX)` + editör `dxImageUpload`;
|
||
değer base64 saklanır, sınırlı boy ve `dxImageViewer` kullanılmaz (deploy ikisini de reddeder).
|
||
2. **Yeni ekran** → Wizard yolu. `Definitions` altı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).
|
||
3. **Yeni ekran/menü** → yetki sözleşmesi olmadan önerilmez. Menü ve route aynı ekran
|
||
sözleşmesini göstermeli.
|
||
4. **Kullanıcıya görünen her metin** → dil anahtarı üzerinden, EN + TR birlikte. Koda gömülü
|
||
metin önerme.
|
||
5. **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.
|
||
6. **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.
|
||
7. **Ziyaretçiye açık yeni yol** (public `RoutePath` taşıyan custom component, `Pub` menüsü,
|
||
yeni public sayfa) → SEO alanları da doldurulur. Ayrı bir SEO tablosu yoktur: meta, yolu
|
||
tanımlayan kaydın `Seo*` kolonlarındadır — `data/App.Menus.Routes.json` ya da
|
||
`data/App.DeveloperKit.CustomComponents/{Id}.json`. Metin `::Anahtar` ile dil anahtarı taşır.
|
||
Kullanıcı SEO bilgisi girmez; üreten yazar. Genel varsayılanlar kayıtta değil
|
||
`App.SiteManagement.Seo.*` ayarlarındadır. Ayrıntı: `lowcode.instructions.md` §0.9.
|
||
8. **Ekran içi hesap/koşul ihtiyacı** → önce `EditorScript` (Script Builder tarifleri), sonra
|
||
`EditorOptions`; kod yazmak son çare.
|
||
9. **SQL** → her zaman parametreli. String birleştirme ile sorgu kurma.
|
||
10. **Silme** → `DeleteCommand = DefaultDeleteCommand("{Tablo}")`; ham SQL string'i yazma.
|
||
11. **Çerçeve sınırı** → uygulama kendini kendi mekanizmalarıyla geliştirir (seed artefaktları;
|
||
var olan `ui` bileşenine eksik olay/prop/küçük yetenek ekleme — komponentin %70'i hazır
|
||
araçlarla kuruluyorsa kalanı bu eklemelerle tamamlanır, yeni bileşen yazılmaz; geriye dönük
|
||
uyumlu tarif/seçenek/token ekleme).
|
||
Bunun dışına çıkan her şey — yeni React bileşeni/sayfası, çekirdek motor, yeni entity/
|
||
migration, platform tablosu şeması, yeni paket, `Routes`, dış entegrasyon — **önce sorulur,
|
||
yalnızca onayla yapılır**; onay talebe özeldir. Çerçeve içinde yapılabilen kısım sorudan önce
|
||
bitirilir. "Yapılamaz" cevabı yoktur. Listeler: `lowcode.instructions.md` §0.8.
|
||
12. **Liste ekranı görünümleri** → prompt/görsel/tarifle üretilen her liste ekranı `Grid` + `Card` +
|
||
`Pivot` + `Chart` açı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ığında `false` yazılır.
|
||
Tree/Gantt/Scheduler/Todo yalnızca istenince açılır. Ayrıntı: `lowcode.instructions.md` §9.8.
|
||
13. **Liste ekranı widget'ları** → Wizard ya da MCP `deploy_wizard` ile üretilen her List ekranı
|
||
sorulmadan **en az 3 kartlık** `Widgets` ile gelir; kartlar tablonun durum / boolean bayrak /
|
||
tarih / tutar kolonlarından seçilir, başlıklar dil anahtarıdır (`App.Widget.*` önce). Kiracı
|
||
tablosunda her kart `"TenantId" = '@TENANTID'` taşır; `IsDeleted` koşulu yazılmaz. Form/Chart,
|
||
menüsüz alt form ve basit tanım listeleri istisnadır. Ayrıntı: `lowcode.instructions.md` §4.6.1.
|
||
|
||
### Cross-cutting: touching one thing means touching these too
|
||
|
||
- **`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.
|
||
- **`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 edilen
|
||
`toolbar` varsayı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ında tekrar yok** → `Wizard` bölümü yalnızca ekrandan üretilemeyen bilgileri
|
||
taşır. `ListForm`/`Fields` içinde karşılığı olan her alan (veri kaynağı, select, anahtar alan,
|
||
kırılım/düzen/düzenleme bayrakları, görünüm option blokları, alt form, widget, iş akışı, alan
|
||
tanımları) dosyaya yazılmadan çıkarılır (`WizardSeedFileDto.ToSeedJson`) ve okurken geri
|
||
doldurulur (`FromSeedJson`). Wizard'a yeni bir alan eklerken karşılığı ListForm'da varsa listeye ekle.
|
||
Dosyada kalan wizard alanları üç başlık altında toplanır — `Menu`, `Language`, `Groups` — ve
|
||
`ListFormCode` → `WizardCode`, `MenuParentModuleId` → `ModuleId`, `LanguageTexts` → `Language.Texts`
|
||
adıyla yazılır; varsayılanı olan alanlar (`CreateMenu: true`, boş `DataSourceConnectionString`, boş
|
||
`MenuParentPath` / `LanguageTexts`) hiç yazılmaz. Bu yalnızca dosya biçimidir; C#
|
||
sözleşmesi düzdür, dönüşüm `WizardSeedFileDto.ToSeedJson` / `FromSeedJson` içindedir. **Wizard
|
||
seed dosyası her zaman bu iki metotla okunup yazılır**; düz `JsonSerializer` çağrısı bloklu
|
||
bölümleri sessizce düşürür.
|
||
- **Wizard ile üretilen ekran** → seed dosyası (`configs/seeds/{kapsam}/wizard/{WizardName}.json`)
|
||
wizard cevaplarının yanında ekranın `GridOptionsEditDto` (`ListForm`) ve alanların
|
||
`ColumnFormatEditDto` (`Fields`) sözleşmesini taşır. ListForm detayından yapılan her kayıt
|
||
(`ListFormsAppService`, `ListFormFieldsAppService`, `ListFormJsonRowAppService`) bu bölümleri
|
||
ve `Wizard` bölümündeki karşılıklarını geri yazar (`WizardSeedSynchronizer`). Bu üçlüden birine
|
||
yeni bir kayıt yolu eklenirse senkronizasyon çağrısı da eklenir; yoksa dosya bayatlar.
|
||
- **Wizard girdisinden ekran üretimi** → tek kaynak `ListFormWizardDto.BuildListForm/BuildFields`,
|
||
entity'ye yazma `GridOptionsEditDto.ApplyTo` / `ColumnFormatEditDto.ApplyTo`. Deploy, seeder ve
|
||
senkronizasyon aynı metotları çağırır; ikinci bir üretim kopyası yazma.
|
||
- **Katman sınırı (DDD)** → `DbMigrator` Application'a referans vermez. Seeder ile AppService'in
|
||
paylaştığı dönüşüm `Application.Contracts`'ta DTO metodudur; entity'ye yazma Domain.Shared'deki
|
||
`IListFormArtifact` / `IListFormFieldArtifact` sö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/App.Languages.LanguageText.json`. Yetki ekranındaki sıra `AbpPermissions.Order`
|
||
(grup için `AbpPermissionGroups.Order`) sütunundan gelir ve seed satırındaki `Order` alanı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.
|
||
- **`/mcp` ucuna yeni bir araç** → üç yer birlikte değişir: `McpToolNames` (ad kayıt defteri),
|
||
ilgili `Mcp*Tools` sınıfı (`Sozsoft.Mcp.HttpApi`) ve arkasındaki AppService sözleşmesi
|
||
(`Sozsoft.Mcp.Application.Contracts`). Arac adı `McpToolNames.All` içinde yoksa araç listesi
|
||
süzgecinden geçmez ve hiç görünmez; MCP Servers ekranındaki `AllowedTools` seçenekleri de
|
||
`SelectableTools`'dan üretildiği için kendiliğinden güncellenir. Sunucu kaydında tek anahtar
|
||
`AllowedTools`'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:** `.Invoke` uca
|
||
erişim ve okumadır (MCP servislerinin sınıf düzeyinde aranır); `.Write` MCP ü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çlar `McpToolNames.TenantTools` ile sınırlıdır (şema, `query_list`, `run_select`,
|
||
`insert_row`/`update_row`/`delete_row`) ve `AllowedTools` bu 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.
|
||
**`/mcp` istek 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: `AddListToolsFilter` listeyi süzer, `McpAppService.EnsureToolAllowedAsync`
|
||
çağrıyı reddeder — süzme tek başına yetki sınırı değildir, ikisi birlikte yazılır. `Invoke`
|
||
uç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.
|
||
**Hata istemciye yalnızca `McpException` olarak ulaşır**; `McpToolErrorTranslator` (çağrı
|
||
filtresi) yetki reddini "şu yetkiniz yok" mesajına, `UserFriendlyException`'ı mesajına çevirir.
|
||
Kullanıcının göreceği her hata bu iki türden biriyle atılır. Yeni aracın arkasındaki servis
|
||
bir yetkiyi `[Authorize]` attribute'uyla istiyorsa (ret mesajı yetki adını taşımaz) o yetki
|
||
`McpToolNames.ServicePermissions`'a eklenir; ekran yetkisi gereken araç yetkiyi
|
||
`IMcpListFormAccessFilter.GetPermissionNameAsync` + `CheckAsync` ile adıyla önceden ister.
|
||
Araç → yetki tablosu `README.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_guide` bö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_wizard` ve 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/App.Menus.{Menu,Module}.json`,
|
||
`data/Abp.Identity.Permission{Groups,s}.json`, `data/App.Languages.LanguageText.json`;
|
||
`WizardSeedRowStore`, anahtarlar `WizardSeedRowKeys`). Kurulum yalnızca DB Migrate'tedir:
|
||
`WizardDataSeeder` ekrana özgü kayıtları (veri kaynağı, ListForm, alan, iş akışı) kurar,
|
||
`ListFormSeedDataApplier` satırları ekler. Deploy/import dosyaya `Pending` yazar; 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 ve `Pending`'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.
|
||
- **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`'a `SeedData: true` ile gider (`SeedFilePath = data/{ListFormCode}.json`
|
||
+ `SeedSync*` açık, `ListFormWizardDto.BuildListForm`) ve satırlar aynı çağrıda `seedRows` ile
|
||
`configs/seeds/host/data/{ListFormCode}.json` dosyasına yazılır (`McpDevelopAppService`; alan adları
|
||
ekrana göre doğrulanır, `Guid` anahtarı 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.
|
||
`SeedData` wizard ekranında sorulmaz ama düzenlemede korunur (`RefreshFrom` + UI modeli).
|
||
Ayrıntı: `lowcode.instructions.md` §0.10.1.
|
||
- **Talepteki görsel** → `App.DevRequests` açıklamasına yapıştırılan görsel base64 `data:` URI olarak
|
||
kalır, hiçbir yere yüklenmez. `start_dev_request` / `get_dev_request` onu metinden ayıklar
|
||
(`McpDevRequestImages`) ve MCP resim içeriği (`ImageContentBlock`) olarak döndürür; metinde
|
||
`[image-N]` kalır. Açıklama bu yüzden `update_row` ile yeniden yazılmaz. `SubTasks` de
|
||
`add_dev_request_subtasks` ile 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_request` talebi etkin talep yapar,
|
||
sonraki her üretim/veri yazma işlemi (`create_table`, `deploy_wizard`, `delete_wizard`,
|
||
`save_component`, `delete_component`, satır araçları, `save_dev_request_analysis`,
|
||
`append_dev_request_note`, `add_dev_request_subtasks`) ona `App.Mcp.Timesheet.*` anahtarından Türkçe
|
||
açıklama ve ölçülen süreyle bir satır ekler; yalnızca talebi açan `start_dev_request` satı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ın `Sas_H_DevRequest` kilidini EF'in ayrı bağlantısından bekletip zaman aşımına düşürür.
|
||
- **Talep analizi** → `Description` talebin yazıldığı hâliyle kalır; MCP ona yazmaz. Analiz ve
|
||
üretim notları `Analysis` kolonundadır (Todo panosunda `AnalysisExpr`, Açıklama yanında sekme).
|
||
Durum sırası `Backlog → Analysis → Ready to Start → …`: "Analiz et" `save_dev_request_analysis`
|
||
ile yalnızca `Backlog`/`Analysis` talebini analiz edip `Analysis`'e alır (hiçbir şey üretmez),
|
||
`Ready to Start`'a kullanıcı taşır, üretim notu `append_dev_request_note` ile `Analysis`'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_DbTableColumn`
|
||
sözlüğünden okunur (`IMcpSchemaDictionary`). Sözlüğün alan adları
|
||
`dbo.Sas_H_ListFormFieldsDictionaryJson` prosedürünün ürettiği JSON ile birebir aynıdır;
|
||
prosedür değişirse `McpDictionaryListForm`/`McpDictionaryField` de değişir.
|
||
- **MCP OAuth** → bilinen sağlayıcılar (Claude, ChatGPT) seed edilmiş `Platform_Mcp` istemcisini,
|
||
diğer MCP istemcileri `/connect/register` ile üretilen `mcp_` önekli açık onaylı istemcileri
|
||
kullanır (`McpOAuthClientManager`; seed `McpOAuthClientDataSeedContributor`). Bilinen sağlayıcı
|
||
eklemek = DbMigrator'daki `Platform_Mcp:RedirectUris` listesine callback eklemek. MCP
|
||
istemcisi kararı tek yerdedir (`McpConsts.IsMcpClient`): `resource` parametresi 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 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_table` bu yüzden `.Write`'a ek olarak `App.SqlQueryManager`
|
||
ister; tablo scriptini tasarımcının `generateCreateTableSql` çıktısıyla aynı biçimde üretir
|
||
(`McpTableCreator`) — tasarımcının şablon kolonları ya da script biçimi değişirse o da değişir.
|
||
- **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ü de `IMultiTenant`) 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
|
||
kopyalanır (`NotificationTenantDefaultsSeedContributor`); kaynak host kapsamındaki seed
|
||
dosyalarıdır, satırlar yeni kimliklerle yazılır. Kiracı verisine dokunan zamanlanmış işin
|
||
`Scope` alanı `Tenant` ya da `Host ve Kiracılar` olmalıdır — `Host` kapsamında çalışan bir iş
|
||
kiracı satırlarını göremez (`WorkerScopeEnum`, `PlatformBackgroundWorker`).
|
||
- **Bildirim bayrakları** → sıra değişmez: `IsSent` (iletildi) olmadan `IsRead` yazılmaz, `IsRead`
|
||
ile `ReadTime` birlikte yazılır. `UiActivity` kayıt oluşurken iletilmiş sayılır, `UiToast`/`Desktop`
|
||
tarayıcıda gösterildiğinde (`NotificationChannels.BrowserChannels`; `sent-many` ucu yalnızca bu
|
||
kümeyi işaretler), kalan kanalları `NotificationWorker` iş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 `/notificationhub` ile
|
||
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 `App.SiteManagement.Seo.*` ayarları, yol bazlı
|
||
alanlar `Route.Seo*` ve `CustomComponent.Seo*` (ikisinin de `SeedSync*` bayrakları açık),
|
||
istemci tarafı `SeoHead`. Üçü de uygulama konfigürasyonunun `seo` alanından beslenir
|
||
(`PlatformApplicationConfigurationContributor` → `SeoAppService.GetConfigAsync`); `sitemap.xml`
|
||
ve `robots.txt` aynı servisten `SeoController` ile 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 (`Pub` menüleri, aktif custom component rotaları, yayınlanmış blog
|
||
yazıları) `SeoAppService` iç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) + `SeoHead` birlikte.
|
||
- **Yeni bir dil anahtarı** → `configs/seeds/host/data/App.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ıflar `ui/safelist.txt`
|
||
sonuna da yazılır (`twSafelistGenerator`, derlemede otomatik); yoksa uygulama sunucuda stilsiz
|
||
kalır. İkinci bir safelist dosyası açma.
|
||
- **Custom component kaydı/silinmesi** → `data/App.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'ler `crud/{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ın `SeedFilePath` veri
|
||
aynası, lookup sorgularının işaret ettiği nesnelerin scriptleri, `crud/` ve
|
||
`{sql|postgres}/{object|execute}/` dosyaları. `Custom` yolunda `data/` 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ü eklenirse
|
||
`WizardImportValidator`'daki karşılığı da eklenir — biri toplar, diğeri eksikliğini yakalar.
|
||
- **Talep bağı (`DevRequest`)** → MCP `deploy_wizard` wizard 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.CollectAsync` birden çok dosya alır). Wizard
|
||
dosyasını yeniden yazan yeni bir yol eklenirse `DevRequest` korunur — 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 tablo `DROP` edilmez. Ölçü farkında (uzunluk/kesinlik/nullability) import durdurulur ve
|
||
fark somut olarak bildirilir; varsayılanı olmayan `NOT NULL` bir 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** → `EnsureImportNotBlocked` ile başlar. Analizdeki
|
||
engelleme kararı sunucuda `blocked` dosyası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`);
|
||
`Error` seviyesinde bir bulgu varsa oturuma `blocked` işareti düşer ve `ApplyImport` reddeder.
|
||
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ına `Pending` yazılır ve ekran deploy'daki gibi DB Migrate'te kurulur;
|
||
dosyadaki `ListForm`/`Fields` sözleşmesi olduğu gibi kullanılır, wizard cevaplarından yeniden
|
||
türetilmez — türetmek tasarımcı değişikliklerini silerdi. Wizard'ın menü/yetki/dil satırlarını
|
||
taşıyan platform dosyaları `data` adı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 bir `Error` eklerken 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 üretirken `TableName` (+ tablo hedefte yoksa `TableMissing`)
|
||
doldurulur, istemci tablo tasarımcısını buna göre açar.
|
||
- **`ListForm.SeedFilePath` dolu bir ekran** → `SeedSyncInsert/SeedSyncUpdate/SeedSyncDelete`
|
||
bayraklarından işaretli olan işlemler kapsam klasöründeki hedefe yansır
|
||
(`ListFormSeedDataSynchronizer`). Hedef iki biçimdedir ve ayrım uzantıdadır: `.json` ile 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; şu an klasör kullanan tek ekran Custom Components). Applier `data/`
|
||
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;
|
||
dosyadaki `ListFormCode` boşsa ekran kodu klasör adından gelir. Tek dosyaya bağlı eski bir kayıt,
|
||
klasörü diskte varsa migrate sırasında klasöre taşınır.
|
||
Migrate/seed sırasında ve wizard import'unun kapanışında
|
||
(`CompleteImport`) `ListFormSeedDataApplier` dosyayı geri uygular. Migrate/seed'de kayıt yoksa
|
||
eklenir, varsa dokunulmaz; import'ta `overwriteExisting` açıktır: var olan kayıt dosyadaki
|
||
değerlerle güncellenir ve soft delete izleri (`IsDeleted`/`DeletionTime`/`DeleterId`) temizlenir. Yeni bir kayıt yolu (yeni endpoint, toplu işlem, tipli AppService) eklenirse
|
||
senkronizasyon çağrısı da eklenir; yoksa dosya bayatlar.
|
||
- **Seed dosyası yazmak** → veri dosyası `ListFormSeedDataFileDto.Serialize`, wizard dosyası
|
||
`WizardSeedFileDto.ToSeedJson` ile yazılır; düz `JsonSerializer.Serialize` + `File.WriteAllText`
|
||
kullanı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 de `SeedJsonText.RestoreSurrogatePairs`'ten
|
||
geçer. Veri dosyasının `GeneratedAt` değeri oluşturma anıdır: `Serialize` yalnı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ın `GeneratedAt` değeri
|
||
kaydın `LastModificationTime`/`CreationTime` değ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ü** → kök menüler `Sas` (`App.Saas`), `Adm`
|
||
(`App.Administration`), `Hr` (`App.Hr`, İK) ve `Pub` modüllerindedir. Bir ekranı modüller arası
|
||
taşımak birlikte yapılır: `TableNameResolver` (`MenuPrefix`) + `AppCodes` (Seed/Platform) +
|
||
`ListFormSeeder_{Saas,Administration,Hr}.cs` + `data/{ListFormCode}.json` adı + menü/yetki/dil
|
||
satırları + route `Authority`. Yetki `Modules` değeri: Saas/Administration `Sas|Adm`, İK `Sas|Hr`
|
||
(`Sas` host, `Adm`/`Hr` kiracı görünürlüğü). İK departman/pozisyon `BranchId` zorunludur; seed
|
||
satırı host şubesine (`PlatformConsts.Branches.BranchIdDefaultValue`) bağlanır.
|
||
- **Menü ağacı ve yetkiler** → `configs/seeds/host/data/App.Menus.{Routes,Module,Menu}.json` ve
|
||
`Abp.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. `Order`
|
||
alanı bağımlılık sırasını kurar: route → modül → menü, yetki grubu → yetki, ülke grubu → ülke →
|
||
şehir → ilçe (`App.Definitions.{CountryGroup,Country,City,District}.json`), bildirim tipi → kuralı
|
||
(`App.Notifications.Notification{Types,Rules}.json`). Para birimi, hitap, AI bot
|
||
(`App.Definitions.{Currency,ContactTitle,AiBot}.json`), ayar tanımları (`App.SettingDefinitions.json`)
|
||
ve zamanlanmış işler (`App.BackgroundWorkers.RecurringJobs.json`), dil → metin
|
||
(`App.Languages.{Language,LanguageText}.json`; `LanguagesData.json` + `LanguageDataSeeder` kaldırıldı) da buradadır; `HostData.json`
|
||
kaldırıldı, `HostDataSeeder` yalnızca bağlantı cümlesi appsettings'ten gelen varsayılan veri kaynağını kurar. Dosyalar ekranın
|
||
anahtarını (`KeyFieldName`) kullanır; `Guid` anahtarlı satırın `Id`'si iş anahtarından türetilmiş
|
||
sabit bir GUID'dir, satır eklerken aynı kuralla üret. `App.Menus.Routes.json` dosyasına
|
||
yalnızca var olan satırın `Seo*` alanları için dokunulur (standing default 7); rota eklemek,
|
||
silmek ya da yolunu değiştirmek çerçeve dışıdır.
|
||
- **Public site** → menü `Menu` tablosunda `ModuleId = "Pub"` altında (`PlatformConsts.PublicSite`);
|
||
`App.Public.Header` hem üst menüyü hem alt bilgideki hızlı bağlantıları besler (ikisi de
|
||
`PublicNav` düğümü) ve ziyaretçiye uygulama konfigürasyonunun `publicMenus` alanıyla taşınır
|
||
(menü ucu `[Authorize]`). Sayfa ise Custom Component'tir: `/admin/` ile
|
||
başlamayan `RoutePath` otomatik public route olur ve aynı yolu paylaşan fiziksel route'u gölgeler;
|
||
`RoutePath` boş bırakılabilir (rotasız bileşen). Yeni public sayfa =
|
||
`data/App.DeveloperKit.CustomComponents/{Id}.json` satırı + `Pub` menü kaydı; ikisi birlikte yazılır.
|
||
Üst ve alt bilgi rotasız bileşenlerdir (`PublicHeader`, `PublicFooter`), layout ikisini de
|
||
`renderComponent` ile çizer; rotasız bileşende `RoutePath` **NULL** yazılır (tekil indeks yalnızca
|
||
dolu değerleri kapsar). Üst bilginin davranışı `components/publicSite` katalog 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ında
|
||
`react-icons`/`react-router` yoktur — ikon `PlatformIcon` düğümü, gezinme `a` düğü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.ts` tanı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` / çoklu `dxGridBox`)** → tek metin kolonunda `|` ile saklanır.
|
||
Ekran varsayılan `list-form-data/*` ucundan kaydediyorsa birleştirmeyi `QueryHelper` yapar;
|
||
tipli DTO alan bir uca (`list-form-dynamic-api/...`, Custom Endpoint, Dynamic Service)
|
||
bağlıysa DTO alanına `[JsonConverter(typeof(MultiValueStringJsonConverter))]` eklenir —
|
||
yoksa istek `400` ile 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.
|
||
- `api/` değişikliğinden sonra: `dotnet build -p:EnforceCodeStyleInBuild=true` — 0 hata, yeni
|
||
`IDE00xx` yok.
|
||
- `ui/` değişikliğinden sonra: `npm run typecheck` ve `npm run lint`.
|
||
- Yorumlar **neden**i 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 `.md`
|
||
dosyası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.
|