Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion TEST-SCENARIOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ geçerliliğini yitiren senaryo **silinmez**, `deprecated` işaretlenip sebebi y
| **attribute-index-preparation (historical)** | Former directly:true automatic job experiment | Superseded by manual DBA decision | Historical evidence only | [README](api-tests/attribute-index-preparation/README.md) | DEPRECATED: previous 6 checks / 13 jobs remain recorded; automatic behavior removed 2026-09-14 |
| **timeout-lab** | Workflow seviyesi `timeout`: state function gövdesindeki **`timeout` bloğu** (`{ key, target, executeAtUtc, annotations }`, `transitions[]` girdisi DEĞİL) · bloğun **etkin** timeout'tan beslenmesi (`subFlow.overrides.timeout ?? workflow.timeout`, tek resolver) · `executeAtUtc`'nin kalıcı `InstanceJob.ExecuteAt`'ten okunması, arm anındaki instant · terminal instance'ta bloğun **asenkron cancel-cleanup zincirini beklemeden** düşmesi · bloğun yalnız poll edilen instance'ı anlatması (aktif subflow'a inmemek, parent'ın child'ın deadline'ını devralmaması) · `ResponseShapeVersion` v9→v10 · **ve fire yolunun override'ı onurlandırması**: child kendi tanımında `"timeout": null` taşırken parent'ın override'ının hedefine çekilmesi | İki ayrı kusur, tek fixture. (1) vnext-client-sdk-core#59: instant zaten job satırında duruyordu ama hiçbir okuma yüzeyi taşımıyordu, client geri sayım çizemiyordu. (2) Council `2026-09-21-state-timeout-surfacing` FACT-A: `subFlow.overrides.timeout` **yalnız arm anında** okunuyordu — job zamanında ateşleniyor, handler child'ın KENDİ tanımını okuyup `TimeoutConfigMissing` ile dönüyordu; yani zamanlanmış ama asla varamayan bir deadline. `subflow-orchestration` tam bu şekli taşıyor ve hiçbir test dokunmuyordu. Ayrıca repoda workflow timeout'unu sınayan **hiçbir** senaryo yoktu ve yazılı iki süre de `PT15M` — bir test koşusunda izlenemez (2026-09-21) | `Tests/TimeoutLab` (1 sınıf, 3 test) + `Tests/TimeoutLab/README.md` | — (bilinçli: doğruluk/regresyon senaryosu, gecikme iddiası yok) | ✅ **Aktif — 3/3 yeşil** (lokal runtime, 2026-09-21). **İlk koşuda kendini amorti etti:** senaryo kırmızı düştü ve konseyin kod okumasının kaçırdığı **dördüncü** bir `workflow.Timeout` okuyucusunu açığa çıkardı — `CreateTransitionRecordStep` `$timeout`'u **order 20**'de `Workflow.ResolveWellKnownKey` ile çözüyor ve override'lı child için `TimeoutNotConfiguredForWorkflowException` **fırlatıyor**, yani pipeline `ApplyTimeoutStateStep` (38) düzeltmeye gelemeden ölüyordu. `SetBusy` (19) çoktan commit ettiği için ölçülen belirti "timeout ateşlenmiyor" değil, child'ın **bekleme state'inde sonsuza dek Busy kalması** oldu (job satırı processed işaretli; `e48ac9e2…` tezgâhta 'öncesi' fotoğrafı olarak duruyor). Kanıt test özetinden değil postgres+loglardan: child `child-timedout`/`C`, audit anahtarı **`child-abandoned`** (PARENT'ın override key'i), armlanan `ExecuteAt`'ten ~54 ms sonra; root `root-timedout`/`C` via `root-abandoned`; logda sıfır `TimeoutConfigMissing`. Komşu senaryolar regresyonsuz: `SubflowOrchestration`+`ChainBusy`+`ScheduleAfterAuto` 39/39. **Annotations (2026-09-24, akışlar 1.0.1, 4/4 yeşil):** gövdenin listelediği her tür — state, shared, `cancel`, `exit`, `updateData` — ve `timeout` bloğu tanımdaki `annotations`'ı taşır; child'ın bloğu parent override'ının annotations'ını taşır (override kaydıyla gider, birleştirilmez). Komşular aynı runtime'da: `SubflowOrchestration`+`ChainBusy` 37/37 |
| **human-task-chain** | `human-task` domain function: aday seçiminin instance'ın KENDİ kolonlarıyla yapılması (`Type IN ('R','P')`, `Status IN ('A','B') AND EffectiveStatus='A'`, `EffectiveStateSubType=6`) · **leaf descent**: seviye-bazında batch, `(domain, flow)` gruplaması, domain sınırı başına tek çağrı · yetkilendirmenin ve `humanTask` metninin **leaf'ten** gelmesi, satır kimliğinin **root'ta** kalması · **SubProcess kimliği**: `P` bağımsız bir akıştır, kendi `Id`'siyle ve **kendi domain'inin** cevabında listelenir, kendi subflow'larına root gibi iner · **yetkilendirme**: kararın leaf state'inin **queryRoles**'undan gelmesi (transition'lar düşürüldü), queryRoles tanımlı değilse **fail-closed** düşme, parent'ın `state_role_overrides`/`transition` override'larının canlı olması (aynı root flow'un iki instance'ı, sadece nerede dinlendikleriyle ayrışıyor), DENY'ın **yalnız başka izinli rol yokken** reddetmesi (`IsAnyRoleAllowed` ilk izinli rolde döner), response cache'inin bir caller'ın listesini diğerine servis etmemesi, SubProcess'in **kendi** leaf'iyle yetkilenmesi · **caller context**: `X-VNext-Cache-Override` ve korelasyon header'larının her domaine geçmesi, truncation'ın agregata yansıması · leaf'in kendi stamped `subflow.transition_role_overrides` haritasından rol çözümleme · `Effective*` invariant'ı: alt akış bitince dörtlünün (state/type/subType/status) parent'ın kendi state'ine dönmesi · projeksiyonun long-poll fingerprint materyali olması | vnext `feature/human-task-function-redesign`: bu uç hiç test edilmemişti ve dört kusur taşıyordu — `EffectiveStateSubType`'ın reset writer'ı yoktu (hayalet human task, herkese, kalıcı), mevcut reset `!HasActiveSubFlow` guard'ı taşımıyordu, descent tek seviye iniyor ve çocuğun tanımını **caller'ın** domain'inde arıyordu (cross-domain çocuk instance'ı listeden tamamen düşürüyordu), iptal edilmiş seviye canlı çocuğun `'A'`'sını ham kolonda tutuyordu (2026-09-17) | `Tests/HumanTaskChain` (2 sınıf, 20 test) | `api-tests/human-task-chain/morph-idm-aggregation-test.py` (20 kontrol, **20/20 geçti** 2026-09-18) + `morph-idm-aggregation.http` (elle sürmek için adım adım) | ✅ **Aktif — 20/20 yeşil** (+ morph-idm uçtan uca 30/30) (dört-domain cross-domain lab, art arda iki koşu, 2026-09-17). Senaryo 3 için lab **dördüncü domain** `credit` (offset 20 → :4221) ile genişletildi; `lab.sh verify` artık ikinci sınırı da (`partner → credit`) doğruluyor. Koşu üç runtime kusuru buldu: fan-out'un içine yerleştirilen özyinelemeli descent aynı DbContext'i paylaşıp 500 veriyordu — DbContext DI scope'una değil **UnitOfWork**'e bağlı ve şema ile anahtarlanıyor, paralel dallar da ambient UoW'yi miras alıyor; her dal artık kendi UoW'sini açıyor (`ExecuteInIsolatedUnitOfWorkAsync`) ve descent paralel kaldı (80 eşzamanlı tam fan-out: 80×200, sıfır istisna), her otomatik geçiş `triggerKind: 10` (DefaultAutoTransition) olmadan rule istiyor, ve rolsüz `cancel` her caller'ı yetkilendirdiği için negatif rol testi anlamsız kalıyordu. SubProcess turu dördüncü bir bulgu ortaya çıkardı, ama bu bir runtime kusuru değil çıktı: state seviyesindeki `subFlow.type: "P"` şekli **geçersiz authoring** olarak yeniden sınıflandırıldı, artık publish anında reddediliyor, ve `ht-a` bir `SubProcessTask`'a dönüştürüldü (bkz. Bilinen Kapsam Açıkları altındaki güncellenmiş satır) |
| **error-boundary-lab** | Error boundary çözümlemesi: `CompiledBoundaryChain` Task→State→Global seviye baskınlığı · seviye içi sıra (`EffectivePriority` ASC → specificity DESC; default wildcard'ın 999'a düşmesi) · aksiyonlar abort/retry/rollback/notify/ignore/log · `TaskExecutionEngine` retry döngüsü (`1 + maxRetries`) ve tükenince `ResolveExcluding(Retry)` fallback'i · `BoundaryOutcomeHandler` → fault vs `RequestNextTransition` · `FinalizeTransitionStep`'in boundary transition'ı bitince incident'ı resolve etmesi · `InstanceIncidents` tablosu + denormalize `HasActiveIncident` · state function `incident` bloğu (link tabanlı, ResponseShapeVersion v9, ETag materyali) · `GET .../instances/{id}/incidents/active` (404 = açık arıza yok) · `GET .../instances/{id}/incidents` sayfalama · `metadata.incident` (state bloğuyla aynı şekil) · `POST .../retry` (400 `Instance:100027`, yeniden fault, veriyle kurtarma) · `queryRoles` kapısının incident geçmişine de uygulanması · **SubFlow fault zinciri** (`eb-sf-root`→`eb-sf-mid`→`eb-sf-leaf`): leaf HTTP 400 abort → mid abort → root global notify ile error-end; root'ta incident'ın boundary verdict'iyle yazılıp **resolve** edilmesi, fault'lanan seviyelerde açık kalması | vnext `feature/incident-table` (issue #865) incident'ları jsonb'den kendi tablosuna taşıdı ve iki yeni client yüzeyi ekledi; ayrıca error boundary bu repoda hiç senaryo olarak yoktu ve `POST .../retry` hiçbir testte çağrılmıyordu (2026-09-06). SubFlow fault zinciri: preprod'dan "subflow'da HTTP 400, ana akış error-end'de, ana akışta incident yok" bildirimi (onboarding `kyc-main-workflow`, 0.0.93) — ölçüldü: incident kayıp değil, resolve (2026-09-24) | `Tests/ErrorBoundaryLab` (7 sınıf, 36 test) | — (bilinçli: davranış/çözümleme senaryosu, eşzamanlılık iddiası yok) | ✅ **Aktif** — `SubFlowFaultChainTests` 5/5 yeşil (vnext `master` @ `f6c5c828`, 2026-09-24); paket 35/36, tek kırmızı `SecuredIncidentsTests` 403 beklentisi vnext #1027 sonrası bayat. Önceki: 31/31 yeşil (lokal runtime `feature/incident-table`, art arda iki koşu, ~1 dk 46 sn; ilk koşu 2026-09-06, üç davranışsal kusurun düzeltilmesinden sonra 2026-09-07'de yeniden) |
| **error-boundary-lab** | Error boundary çözümlemesi: `CompiledBoundaryChain` Task→State→Global seviye baskınlığı · seviye içi sıra (`EffectivePriority` ASC → specificity DESC; default wildcard'ın 999'a düşmesi) · aksiyonlar abort/retry/rollback/notify/ignore/log · `TaskExecutionEngine` retry döngüsü (`1 + maxRetries`) ve tükenince `ResolveExcluding(Retry)` fallback'i · `BoundaryOutcomeHandler` → fault vs `RequestNextTransition` · `FinalizeTransitionStep`'in boundary transition'ı bitince incident'ı resolve etmesi · `InstanceIncidents` tablosu + denormalize `HasActiveIncident` · state function `incident` bloğu (link tabanlı, ResponseShapeVersion v9, ETag materyali) · `GET .../instances/{id}/incidents/active` (404 = açık arıza yok) · `GET .../instances/{id}/incidents` sayfalama · `metadata.incident` (state bloğuyla aynı şekil) · `POST .../retry` (400 `Instance:100027`, yeniden fault, veriyle kurtarma) · `queryRoles` kapısının incident geçmişine de uygulanması · **SubFlow fault zinciri** (`eb-sf-root`→`eb-sf-mid`→`eb-sf-leaf`): leaf HTTP 400 abort → mid abort → root global notify ile error-end; root'ta incident'ın boundary verdict'iyle yazılıp **resolve** edilmesi, boundary handling içindeki mapping'in `context.Incident.ActiveIncident`'ı görmesi, fault'lanan seviyelerde açık kalması | vnext `feature/incident-table` (issue #865) incident'ları jsonb'den kendi tablosuna taşıdı ve iki yeni client yüzeyi ekledi; ayrıca error boundary bu repoda hiç senaryo olarak yoktu ve `POST .../retry` hiçbir testte çağrılmıyordu (2026-09-06). SubFlow fault zinciri: preprod'dan "subflow'da HTTP 400, ana akış error-end'de, ana akışta incident yok" bildirimi (onboarding `kyc-main-workflow`, 0.0.93) — ölçüldü: incident kayıp değil, resolve (2026-09-24) | `Tests/ErrorBoundaryLab` (7 sınıf, 36 test) | — (bilinçli: davranış/çözümleme senaryosu, eşzamanlılık iddiası yok) | ✅ **Aktif** — `SubFlowFaultChainTests` 5/5 yeşil (vnext `master` @ `f6c5c828`, 2026-09-24); paket 35/36, tek kırmızı `SecuredIncidentsTests` 403 beklentisi vnext #1027 sonrası bayat. Önceki: 31/31 yeşil (lokal runtime `feature/incident-table`, art arda iki koşu, ~1 dk 46 sn; ilk koşu 2026-09-06, üç davranışsal kusurun düzeltilmesinden sonra 2026-09-07'de yeniden) |
| **event-driven-lab** | Olay tetiklemeli start + `triggerType: 3` transition · Dapr Subscription route'u · CloudEvent açma · `IEventMapping` ile iş anahtarı korelasyonu · eşleşmeyen olayın ack'lenmesi | `POST .../instances/events` ucunun **hiç testi yoktu** (council `2026-09-11-route-trace-coverage`, adı konmuş boşluk); ayrıca `Event.Intake` span'i bu ucu ölçüyor ve testsiz route'a span gönderilmez (2026-09-13) | `Tests/EventDrivenLab` (1 sınıf) | — | ✅ Aktif |
| **chain-busy** | Accept-time subflow chain reserve **ve başarısız forward'da geri alınması (E31)** · Busy-as-mutex · `$self` shared transition vs `updateData` lifecycle sınırı · start `initial → initial` semantiği · cancel propagasyonu (in-process ↕ distributed) · scheduled transition re-arm | `updateData`-only self-target profil sınırını pinlemek — `target: $self` "hook'ları atla" demek değil (2026-08-17). **E31 (2026-09-12):** post-commit forward istemci hatasıyla düşünce rezervasyon salınmıyordu; üretimde 30 günde 51 mahsur instance (council `2026-09-08-parent-notification-mechanism`, P0) | `Tests/ChainBusy` (5 sınıf) | `api-tests/chain-busy/chain-busy-behaviour-test.py`, `chain-busy-accept-test.py` | ✅ Aktif |
| **script-race-lab** | Script engine: paylaşılan `AssemblyLoadContext`'te çift-derleme yarışı · `scripts.helpers` · subflow output mapping · parent kalıcı fault riski | `Script_XXXX already loaded` / `FileLoadException` yarışının fixture'ı; fix'siz runtime'da kaybedenler parent'ı kalıcı fault'lar (2026-08-18) | `Tests/ScriptRaceLab` | `api-tests/script-race-lab/race-load.py` (yük), `publish.py` | ✅ Aktif |
Expand Down
18 changes: 18 additions & 0 deletions core/Tasks/error-boundary-lab/eb-incident-probe-task-2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"key": "eb-incident-probe-task-2",
"version": "1.0.0",
"domain": "core",
"flow": "sys-tasks",
"flowVersion": "1.0.0",
"tags": [
"integration-test",
"error-boundary-lab",
"script-task",
"incident-probe",
"subflow-fault-chain"
],
"attributes": {
"type": "7",
"config": {}
}
}
18 changes: 18 additions & 0 deletions core/Tasks/error-boundary-lab/eb-incident-probe-task.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"key": "eb-incident-probe-task",
"version": "1.0.0",
"domain": "core",
"flow": "sys-tasks",
"flowVersion": "1.0.0",
"tags": [
"integration-test",
"error-boundary-lab",
"script-task",
"incident-probe",
"subflow-fault-chain"
],
"attributes": {
"type": "7",
"config": {}
}
}
15 changes: 12 additions & 3 deletions core/Workflows/error-boundary-lab/build-error-boundary-lab.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@
# handler runs on failed attempts too, so the old httpSucceeded stamp was true even when
# the call had failed; httpAttempts/httpLastStatus are what actually discriminate, and
# httpAttempts is how the retry tests prove a recovery without reading MockLab's logs.
VERSION = "1.1.0"
# 1.2.0 — eb-sf-root carries an incident probe on its boundary transition and error-end OnEntry
# (context.Incident as a mapping sees it while the root handles a subflow fault).
VERSION = "1.2.0"

VIEWER_ROLE = "eb-lab.viewer"

Expand Down Expand Up @@ -367,6 +369,7 @@ def terminal(key, text, sub_type):
# afterwards — in particular the root's, which handled the fault instead of faulting.
SF_RULE = "EbAlwaysTrueRule.csx"
SF_MAPPING = "EbSubFlowPassMapping.csx"
PROBE_MAPPING = "EbIncidentProbeMapping.csx"


def auto(key, target, text):
Expand Down Expand Up @@ -450,13 +453,19 @@ def sf_workflow(key, text, states, start_target, boundary, extra=None):
subflow_state("r-in-mid", "Root: waiting on the mid", "eb-sf-mid",
[auto("r-mid-done", "r-done", "Root: mid finished")]),
terminal("r-done", "Root done", 1),
terminal("r-error-end", "Root error end", 2),
# The OnEntry probe records what context.Incident shows AFTER the state change, still
# inside the boundary transition's hop (FinalizeTransitionStep resolves it after this).
dict(terminal("r-error-end", "Root error end", 2),
onEntries=[hook(1, "eb-incident-probe-task-2", PROBE_MAPPING)]),
],
"r-initial",
# Same shape as the preprod root: a notify with a transition, so the root HANDLES the fault.
[{"action": 4, "transition": "r-has-error", "priority": 10}],
extra={"sharedTransitions": [
dict(transition("r-has-error", "r-error-end", "Root: a subflow faulted"),
# The OnExecute probe records what context.Incident shows to the boundary transition's
# own mapping — the place a domain developer branches on the subflow's failure.
dict(transition("r-has-error", "r-error-end", "Root: a subflow faulted",
[hook(1, "eb-incident-probe-task", PROBE_MAPPING)]),
availableIn=["r-in-mid"]),
]},
)
Expand Down
2 changes: 1 addition & 1 deletion core/Workflows/error-boundary-lab/eb-sf-leaf.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"flow": "sys-flows",
"flowVersion": "1.0.0",
"domain": "core",
"version": "1.1.0",
"version": "1.2.0",
"tags": [
"integration-test",
"error-boundary-lab",
Expand Down
4 changes: 2 additions & 2 deletions core/Workflows/error-boundary-lab/eb-sf-mid.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"flow": "sys-flows",
"flowVersion": "1.0.0",
"domain": "core",
"version": "1.1.0",
"version": "1.2.0",
"tags": [
"integration-test",
"error-boundary-lab",
Expand Down Expand Up @@ -97,7 +97,7 @@
"process": {
"key": "eb-sf-leaf",
"domain": "core",
"version": "1.1.0",
"version": "1.2.0",
"flow": "sys-flows"
},
"mapping": {
Expand Down
Loading
Loading