MAT-004: Public API And Client State Contract
Geliştirme · 0.0.0-dev
Yayın
- Doküman
- 0.0.0-dev
- Uygulama
- 0.0.0
Bu sayfa
- Uygulama
- 0.0.0
- Status: Accepted
- Date: 2026-08-18
- Decision owners: architecture, API, frontend and security maintainers
- Scope: public API v1 operation names, envelopes, pagination, client states, generated contracts and query budgets
Amaç ve sınır
Bölüm başlığı “Amaç ve sınır”ADR-010 public API’nin versioned, allowlist
tabanlı, release-bound ve RLS ile korunan olmasını şart koşar. Bu belge onun açık
bıraktığı concrete wire sözleşmesini kapatır. Machine key’ler ve sayısal bütçeler
config/governance/api-contract.yml içindedir; bu belge onların semantik anlamını
açıklar.
Bu karar PostgreSQL tablolarını, migration’ı, RPC gövdelerini veya frontend component’lerini üretmez. F3/F4 storage ve RPC’leri, F8 generated client ve UI durumlarını bu sözleşmeden üretir. Public JSON hiçbir zaman MAT-003 fiziksel relation shape’inin doğrudan serialization’ı değildir.
Public yüzey ve adlandırma
Bölüm başlığı “Public yüzey ve adlandırma”İlk major’ın exposed schema’sı api_v1 olur. Supabase/PostgREST çağrısı
/rest/v1/rpc/{rpc_name} yolunu ve api_v1 profile’ını kullanır. Major, operation
dispatch edilmeden önce schema ve fiziksel adın _v1 son ekiyle iki kez belirgindir.
Profile veya suffix uyuşmazlığı operation lookup yapılmadan unsupported_contract
döndürür. Unversioned alias ve function overload yasaktır.
Üç kimlik birbirinden ayrılır:
| Kimlik | Ömür | Örnek |
|---|---|---|
| Logical operation key | Capability değişmedikçe kalıcı | entity_exact_get |
| Operation revision | Request/response/auth/error semantiğinin immutable sürümü | deployment içindeki revision UUID |
| Physical RPC name | Bir major içindeki unambiguous PostgREST adı | get_entity_exact_v1 |
Storage relation, helper function, cache veya private provenance adı public operation
olamaz. OpenAPI ve generated client yalnız accepted exposure manifest’in
public_surface bölümünden üretilir.
V1 operasyonları
Bölüm başlığı “V1 operasyonları”| Logical operation | RPC | Amaç | Erişim | Sayfalama |
|---|---|---|---|---|
contract_capabilities_get |
get_contract_capabilities_v1 |
Desteklenen major/release/deployment keşfi | public | Yok |
chapter_core_get |
get_chapter_core_v1 |
Annotation’dan bağımsız exact chapter core projection | public | Yok |
chapter_headings_get |
get_chapter_headings_v1 |
Release-pinned stable heading hiyerarşisi ve chapter TOC projection’ı | public | Yok |
chapter_ottoman_get |
get_chapter_ottoman_v1 |
Core koordinatlarına bağlı exact Osmanlıca reader unit’leri | public | Yok |
chapter_hover_get |
get_chapter_hover_v1 |
Token/terkip hover binding ve lexical child projection’ları | public | Yok |
chapter_notes_get |
get_chapter_notes_v1 |
Exact marker geometry, accepted note body ve explicit source-gap projection’ı | public | Yok |
entity_exact_get |
get_entity_exact_v1 |
İstenen historical entity’yi yönlendirmeden getirir | public | Yok |
entity_current_resolve |
resolve_entity_current_v1 |
Explicit policy ile current target kümesini çözer | public | Yok |
entity_occurrences_list |
list_entity_occurrences_v1 |
Token, compound veya başka entity’nin geçtiği yerler | public | Cursor |
repetition_members_list |
list_repetition_members_v1 |
Exact class veya repeat family üyeleri | public | Cursor |
alignment_targets_list |
list_alignment_targets_v1 |
Dil çiftindeki görünür hedefler | public | Cursor |
annotations_list |
list_annotations_v1 |
Owner’a bağlı yayınlanmış annotation projection’ları | public | Cursor |
public_provenance_get |
get_public_provenance_v1 |
Disclosure-approved source/release özeti | public | Yok |
curation_proposal_get |
get_curation_proposal_v1 |
Yetkili kullanıcının exact proposal kaydı | authenticated/curator | Yok |
curation_proposal_submit |
submit_curation_proposal_v1 |
Immutable proposal payload’ını dondurup sunar | eligible authenticated | Yok |
curation_review_record |
record_curation_review_v1 |
Frozen revision için review yazar | eligible authenticated | Yok |
curation_decision_record |
record_curation_decision_v1 |
Review kümesine bağlı decision yazar | eligible authenticated | Yok |
curation_decision_apply |
apply_curation_decision_v1 |
Accepted decision’ı atomik ve exact-once uygular | eligible authenticated | Yok |
public burada anon, authenticated ve app_runtime rollerinin
visibility filtresinden geçmiş aynı contract shape’ini ifade eder. Her görünür source,
related entity’yi görünür yapmaz; her member ayrıca filtrelenir. Curation write
operasyonları yalnız authenticated request role’üne grant edilir, fakat bu rol tek
başına curation yetkisi değildir. Routine güncel actor capability, revocation, policy
ve freeze epoch’u DB içinde yeniden doğrular; internal curation_executor public caller
olmaz. İşlemler doğrudan tablo DML’i değildir ve idempotency_key zorunludur. Proposal
düzeltmesi reviewed byte’ı değiştirmez; expected_parent_id bağlı yeni successor
proposal olarak yeniden sunulur.
Bu ayrım machine contract’taki authorization_profile ile zorunludur. Public read
release_visibility; curation read güncel subject/eligibility/revocation; curation
write bunlara ek authority epoch, freeze epoch ve exact policy release kontrollerini
aynı transaction’da yapar. Operation’daki caller role ile profile role’ü farklıysa
contract yayımlanamaz.
Proposal submission, payload’ı DB’ye yazmadan önce curation-proposal-json-v1 ile
canonicalize eder. Request; payload, action, target ve evidence inventory root’larıyla
expected-state, authority-snapshot ve policy root’larını birlikte taşır. Persist edilen
revision insert-only’dir; review proposal_revision_id + payload_root çiftine bağlanır.
Değişiklik aynı row’u güncellemez, exact parent revision’a bağlı successor submission
üretir.
Release-bound response
Bölüm başlığı “Release-bound response”Dispatch başarılı olduğunda transaction başında şu context tek tuple olarak pinlenir:
api_deployment_idapi_contract_release_idapi_operation_revision_idcode_release_idschema_release_iddata_release_set_rootpolicy_release_set_rootexposure_manifest_rootClient bu parçaları bağımsız seçip geçerli olmayan bir bileşim üretemez. Historical pin yalnız accepted deployment compatibility kaydının exact tuple’ına eşitse kullanılır. Her continuation aynı context’i taşır; yeni release’e sessiz fallback yoktur.
Başarı envelope’u exact olarak şunları taşır:
{ "request_id": "uuid", "operation_id": "entity_occurrences_list", "result_state": "available", "state_context": { "reason_code": null, "retry_after_ms": null, "status_resource_id": null }, "release_context": {}, "data": [], "page": { "next_cursor": "opaque", "has_more": true }}page sayfalanmayan operation’da da shape drift yaratmamak için null olarak bulunur.
Failure envelope’u request_id, operation_id, error ve release_context taşır.
Release context yalnız dispatch öncesi reddedilen istekte null olabilir. Internal SQL,
relation, policy expression, source locator, actor veya private evidence detail’i iki
envelope’a da girmez.
Her operation data_cardinality: object | list bildirir. Cursor kullanan operation’ın
data alanı daima response item type’ının array’idir; diğerleri object’tir. Optionality,
envelope alan tipleri ve nested public projection type’ları machine contract’ta explicit
type spec olarak bulunur. Generator singular response_type adından cardinality tahmin
etmez. Envelope’daki operation_response_data serbest generic veya JSON değildir;
operation’ın exact response_type ve data_cardinality alanlarından türetilir,
nullability’si state payload contract’ına ve transitive public type closure’a bağlıdır.
Sonuç ve UI durumları
Bölüm başlığı “Sonuç ve UI durumları”HTTP başarı, domain sonucunun available olduğu anlamına gelmez. Başarılı operation
aşağıdaki discriminated state’lerden tam birini döndürür:
| Result state | Data kuralı | UI state | Anlam |
|---|---|---|---|
available |
Dolu typed data | content |
İstenen görünür projection hazır |
empty |
Typed boş değer | empty |
Sorgu tamamlandı ve görünür üye yok |
pending |
Data yok | pending |
İş devam ediyor; retry süresi veya status resource zorunlu |
unavailable |
Data yok | unavailable |
Bu istek için terminal; capability bu tuple/policy altında sunulamıyor |
ambiguous |
Görünür typed target kümesi | ambiguous |
Tek hedef iddiası yapılamıyor |
retired |
Lifecycle summary | retired |
Exact kayıt var, current kullanım dışı; sessiz redirect yok |
loading server state değildir. Yalnız tamamlanmamış yerel request’in geçici UI
durumudur. Her terminal response loading’i bitirir. Böylece boş anlam, unavailable
annotation veya bir RPC hatası sonsuz “yükleniyor” görünümüne dönüşmez.
pending cevabı da mevcut network loading’ini bitirir ve ayrı, bounded polling state’ine
geçirir; retry süresi veya status resource olmadan pending üretilemez.
state_context, reason_code, retry_after_ms ve status_resource_id alanlarını her
zaman aynı shape’te taşır. pending en az retry süresi veya status resource vermek
zorundadır; non-retryable state retry_after_ms veremez.
Machine contract bu yapıyı kapalı discriminated union olarak tanımlar. empty yalnız
cursor listelerinde zero-length typed array; ambiguous en az iki görünür candidate’ın
operation’a özgü path’i; retired ise operation’a özgü lifecycle path’i ile geçerlidir.
Bilinmeyen state contract violation ve release blocker’dır; generated TypeScript switch
never exhaustiveness kontrolünü geçmek zorundadır.
Error sözleşmesi
Bölüm başlığı “Error sözleşmesi”Error envelope code, retryable, detail_code ve retry_after_ms alanlarından
oluşur. Kapalı public code kümesi:
| Code | HTTP | Client state | Not |
|---|---|---|---|
invalid_request |
400 | error |
Yalnız validation code görünür |
unauthenticated |
401 | signed_out |
Kimlik detail’i yok |
not_found_or_forbidden |
404 | not_found |
Yokluk ve yasak public caller için ayırt edilemez |
unsupported_contract |
406 | update_required |
Yalnız desteklenen major metadata’sı |
conflict |
409 | conflict |
Curation/concurrency code’u, private detail yok |
continuation_invalid |
409 | refresh_required |
Cursor nedeninin ayrıntısı açıklanmaz |
response_budget_exceeded |
422 | unavailable |
Complete projection contract limitine sığmadı; partial body yok |
rate_limited |
429 | retrying |
Yalnız retry süresi |
integrity_failure |
503 | unavailable |
Yalnız public incident ID |
service_unavailable |
503 | unavailable |
Retry policy’si taşır |
timeout |
504 | retrying |
Bounded timeout; geniş sorguya fallback yok |
Public API’de ikinci bir 404 code’u eklenemez. Restricted audit gerçek absent/forbidden sebebini ayrı private evidence olarak tutabilir; public body, count, timing veya cursor bu ayrımı sızdıramaz.
Cursor sözleşmesi
Bölüm başlığı “Cursor sözleşmesi”Bütün listeler forward-only, opaque ve authenticated cursor kullanır. Default page 50,
üst sınır 100, default total count yoktur. Her list operation kendi response item’ında
bulunan UUID order_keys dizisini bildirir: occurrence için sentence_id, occurrence_id,
repetition için member_id, alignment için target_unit_id, alignment_group_id,
annotation için annotation_id. Gizli storage alanı cursor order’ına sokulamaz.
Cursor’ın doğrulanmış içeriği şunlara bağlıdır:
- exact deployment, contract ve operation revision;
- exact data ve policy release set root’ları;
- canonical query shape hash’i;
- authorization subject ve visibility scope hash’leri;
- son order key, page size ve expiry.
Cursor en fazla 15 dakika geçerlidir. Signature, operation, release, query, principal,
visibility veya page-size eşleşmezse continuation_invalid döner. Cursor içeriği public
authority değildir ve client tarafından düzenlenemez. Bir kullanıcıya ait continuation
başka kullanıcı, policy veya yeni deployment altında kullanılamaz.
Generated type zinciri
Bölüm başlığı “Generated type zinciri”Canonical üretim yönü tektir:
accepted API contract + exposure_manifest.public_surface -> accepted OpenAPI projection -> TypeScript API types + typed operation clientHer generated artifact generator ID/version, API contract root, exposure manifest root
ve OpenAPI root header’ı taşır. Elle değişiklik, private dependency type’ının output’a
girmesi veya yeniden üretimde byte drift release blocker’dır. TypeScript union’ları
result_state ve error code için exhaustive olmalıdır; default ile bilinmeyen state’i
empty ya da unavailable saymak yasaktır.
Nested type reference’ları yalnız machine contract’taki public_type_dependencies
allowlist’inden gelir ve her biri exposure_manifest.public_surface üyeliğini kanıtlar.
core.*, provenance.* gibi storage çağrışımlı namespace veya unconstrained
json_object transitive closure’a giremez. Entity, annotation ve curation payload’ları
da isimlendirilmiş public projection schema’larıdır; private alan sonradan filtrelenmez.
Query ve performans bütçeleri
Bölüm başlığı “Query ve performans bütçeleri”Bütçe bir internet latency SLA’sı değil, aynı fixture ve deployment profile’ında ölçülen DB/reponse release kapısıdır. Operation daha pahalı davranacaksa gizli biçimde limiti artırmak yerine contract/fixture review gerekir.
| Profil | Max item | Max body | Max DB statement | DB p95 hedefi | Statement timeout |
|---|---|---|---|---|---|
point_read |
1 | 256 KiB | 8 | 100 ms | 1,000 ms |
chapter_read |
50,000 | 16 MiB | 12 | 350 ms | 2,500 ms |
bounded_list |
100 | 512 KiB | 8 | 150 ms | 1,500 ms |
graph_list |
100 | 1 MiB | 12 | 250 ms | 2,000 ms |
curation_write |
1 | 256 KiB | 20 | 500 ms | 3,000 ms |
Chapter bütçesindeki item sayısı public pagination anlamına gelmez; bir accepted chapter
projection içindeki bounded structural unit tavanıdır. Limit aşılırsa partial chapter
döndürülmez: serialization başlamadan response_budget_exceeded üretilir. Core
reconstruction invariant’ını koruyan yeni contract revision veya stream/package taşıma
stratejisi gerekir. Machine budget_enforcement bütün response’u ölçer ve chapter’ı
all_or_nothing olarak işaretler.
F8-003 corpus üst sınırı incelemesinde accepted kaynak kümesinin en büyük chapter’ı 40.314 token occurrence, 3.313 sentence ve 1.042 paragraph olarak ölçülmüştür. Bütçe bu üç array’in toplamı olan 44.669 structural item’ı sayar. Public projection’ın UUID ve bütünlük alanlarıyla tahmini boyutu 11.351.169 bayttır; eski 25.000 / 4 MiB sınırı bu ve başka büyük chapter’ları eksiksiz reader kapsamının dışında bırakıyordu. 50.000 / 16 MiB sınırı mevcut corpus üst sınırına açık marj ekler. Response shape, hata anlamı ve all-or-nothing davranışı değişmediği için bu genişletme aynı major içinde compatible capability release’idir; eski deployment kendi eski bütçesiyle exact olarak pinlenebilir. Bu pin yalnız rakamları değil sayım semantiğini de korur: v1.0 limiti yalnız token occurrence sayar, v1.1 ise paragraph + sentence + token occurrence toplamını structural item olarak sayar. F8-015 ölçüm sonucunda daha verimli bir taşıma gerekirse bu, mevcut chapter’ı sessizce bölmeden yeni bir contract revision olarak ele alınacaktır.
N+1, limitsiz recursive traversal, exact count için tam corpus scan ve timeout sonrası daha geniş query fallback’i yasaktır. Explain/statement count fixture’ları private schema detayını public response’a taşımadan release evidence’ına yazılır.
Compatibility projection
Bölüm başlığı “Compatibility projection”Contract diff aşağıdaki olgulara mekanik çevrilir:
- yeni operation veya optional response alanı:
additive_optional_contract_behavior; - required alan ekleme, alan kaldırma, type/cardinality veya result/error anlamı
değiştirme:
required_public_contract_behavior_removed_or_reinterpreted; - authorization zayıflatma:
authorization_or_disclosure_weakened_incompatibly; - desteklenen tuple’ı sınır/successor olmadan kaldırma:
supported_release_tuple_invalidated_without_successor_or_major_boundary.
Bu projection MAT-002 evaluator’ına gider. “Generated diff küçük” veya “yalnız RPC rename” compatibility class’ını düşürmez.
Bir operation yalnız machine contract’ta rezerve edilmiş, fakat önceki hiçbir accepted contract release’inde operation revision ve executable deployment kazanmamışsa henüz desteklenen response davranışı oluşturmaz. İlk activation paketi request/response şeklini düzeltebilir ve bunu aynı major içindeki ilk capability release’i olarak yayımlayabilir; bunun kanıtı predecessor deployment envanteri ve bağımsız schema review’udur. İlk accepted operation revision’dan sonra bu istisna biter ve yukarıdaki normal type/cardinality compatibility kuralları eksiksiz uygulanır.
Uygulama handoff’u
Bölüm başlığı “Uygulama handoff’u”- F3-019 machine contract’taki exposed schema, operation revision, manifest ve release context alanlarını migration ve FK/check’lere dönüştürür.
- F4/F5/F6/F7 domain projection’ları yalnız bu operation type boundary’leri üzerinden public olur; yeni storage relation public endpoint yaratmaz.
- F8 generated OpenAPI/TypeScript artifact’larını, exhaustive query state reducer’ını, cursor yenilemesini ve bütçe telemetry fixture’larını uygular.
- F10 deployment tuple, exposure manifest, contract diff ve performance evidence’ını release authorization’a bağlar.
Uygulama testleri en az aynı operation’ın iki principal’la cursor reuse reddini, sayfa arasında release değişimini, empty/pending/unavailable ayrımını, forbidden/absent public eşdeğerliğini, generated type drift’ini, bounded chapter reconstruction’ı ve her bütçe profilinin fixture ölçümünü kanıtlamalıdır.
Sonuçlar
Bölüm başlığı “Sonuçlar”Kazanımlar
Bölüm başlığı “Kazanımlar”- DB, web ve dokümantasyon aynı operation/state adlarını kullanır.
- Frontend eksik data’yı sonsuz loading veya yanlış empty state olarak yorumlamaz.
- “Geçtiği yerler”, tekrar ve multilingual listeleri release/policy değişiminde karışmaz.
- Storage refactor public JSON’u istemeden değiştiremez.
- Performans beklentileri ancak production sorunu çıktıktan sonra değil release öncesinde ölçülebilir hale gelir.
Maliyetler
Bölüm başlığı “Maliyetler”- Yeni public capability operation allowlist ve generated contract release’i ister.
- Cursor key rotation ve historical deployment doğrulaması server-side state gerektirir.
- Role-matrix, state, pagination ve budget fixture’ları sıradan endpoint testinden daha geniştir.
Bu maliyetler accepted identity ve reference ağının sessiz API drift’ine uğramasını engelleyen zorunlu sınırdır; runtime’a gereksiz domain soyutlaması eklemez.