think
16px
820px

PT Flow Migration Plan — old-style → v2-generic, + legacy ports

Date: 2026-07-09 · Branch context: refactor/tidyup-foundation (tidy) vs feat/pt-flows-registry (legacy checkout of ahu-ocr-akta-notaris)


0. TL;DR

Current priority (Efran, 2026-07-09): 1. All PP flows → 2. All Apostille/Legalisasi flows → 3. All PT flows. This inverts the PT-first framing of the original draft — sequencing in §5 below is now ordered accordingly.

Three kinds of work:

  1. Transform old-style → new-style — flows that run on their own bespoke, hand-written processor (v1-fork) get rebuilt as a declarative FlowConfig fed to the one shared runFlow engine (v2-generic). Behaviour must be byte-identical, proven by a parity oracle, then smoke-tested.
  2. Port legacy-only flowsPEMBUBARAN_PT and BERAKHIRNYA_STATUS_PT were already built as v2-generic on feat/pt-flows-registry, but that branch is 284 commits behind tidy, so they need interface reconciliation before landing.
  3. Build a genuinely missing flowPENGGABUNGAN (merger) is a real SABH transaction type (id_aksi_transaksi=11, confirmed §2.1) that no branch has ever built. Not greenfield speculation — a confirmed gap.

Confirmed truly out of scope: PEMISAHAN (spin-off) is not a real SABH transaction type — no id_aksi_transaksi corresponds to it anywhere in TransaksiPerseroan::getJenisTransaksi(). There is nothing to migrate or build; drop it from consideration entirely (see §2.1).


1. Why "Peleburan / Laporan RUPS" are v2-generic but "Pendirian / Perubahan / PP" are old-style

This trips people up, so state it plainly: the label is about which execution engine the flow runs on — not how mature, correct, or well-tested it is.

Two engines live in the codebase right now

flowchart TB subgraph dispatch["dispatchProcessor(type) — services/submission-dispatch.ts"] Q{"flowFor(type).engine
=== 'v2-generic' ?"} end Q -->|"YES"| GEN Q -->|"NO (v1-fork)"| FORK subgraph GEN["v2-generic — ONE shared engine"] direction TB R["runFlow(id, cfg)
flow-engine/processor.ts"] C["FlowConfig (data, not code)
defineFlow({ primaryAkta, companyLookup,
extraction, rules[], sections[], rematch, hooks })"] C -. "config drives" .-> R R --> GT["tenants: PELEBURAN_PT · LAPORAN_RUPS_TAHUNAN"] end subgraph FORK["v1-fork — one bespoke processor PER flow"] direction TB P1["submission-processor.ts (PENDIRIAN_PT)"] P2["perubahan-processor.ts (PERUBAHAN_PT)"] P3["akuisisi-processor.ts (AKUISISI_PT)"] P4["pp-*-processor.ts (PP family)"] P5["apostille-processor.ts (2-phase)"] end
  • v1-fork ("old-style"): each flow is imperative code. It has its own dedicated processor (perubahan-processor.ts, pp-perubahan-processor.ts, …) and its own fat route (routes/perubahan.ts = 1763 lines, routes/pp-perubahan.ts = 1668 lines). dispatchProcessor hard-switches on the SubmissionType to the matching function. Every flow re-implements the same pipeline (extract → company lookup → rematch → validate → sections → finalize), so cross-flow consistency drifts and every fix is copy-pasted N times.
  • v2-generic ("new-style"): the flow is declarative data. A defineFlow({...}) config object (FlowConfig in flow-engine/types.ts) describes the flow; the single runFlow() engine executes it. dispatchProcessor short-circuits if (entry.engine === 'v2-generic') return runFlow(...) before the per-type switch. Routes are thin (peleburan.ts = 398, laporan-rups.ts = 229) because the generic engine + generic route families carry the weight.

The apparent paradox — "I've smoke-tested the old-style ones, never the new-style ones"

That's expected, because "new-style" ≠ "more proven." There are two independent axes:

Axis Pendirian / Perubahan / PP family Peleburan / Laporan RUPS
Architecture old-style (bespoke v1-fork) new-style (declarative v2-generic)
Maturity / real-world smoke high — oldest flows, Efran-driven low — never smoke-tested

They're inversely correlated right now, and that's the normal shape of a mid-migration codebase: you build the new engine, prove it on two new, low-risk pilot flows (Peleburan, Laporan RUPS), and only then migrate the big, mature flows onto it. The mature flows are "old-style" precisely because they predate the engine — not because anything's wrong with them.

Correction vs. an earlier draft of this plan: AKUISISI_PT is not in the "heavily smoke-tested" bucket with Pendirian/Perubahan/PP. Efran confirmed (2026-07-09): all legacy (v1-fork) flows are smoke-tested EXCEPT Akuisisi, Peleburan, and Laporan RUPS Tahunan. Project docs corroborate: the 2026-07-03 ReviewData plan explicitly lists services/akuisisi-processor.ts alongside routes/peleburan.ts/routes/laporan-rups.ts as "un-smoke-tested new PT flows" to leave untouched. So Akuisisi is old-style and unproven — treat it with the same caution as Peleburan/RUPS, not as a battle-tested donor.

Guardrail that falls out of this: before piling more flows onto the generic engine, smoke-test Peleburan + Laporan RUPS (and Akuisisi, in its current v1-fork form) — Peleburan/RUPS are the only evidence the v2-generic engine works end-to-end on a real submission, and neither has been driven yet. Validating the engine first de-risks every migration wave below.


2. Three independent axes — don't conflate them

Every PT flow moves along three separate tracks, each with its own registry/mechanism and its own migration order. A flow can be ahead on one axis and behind on another:

Axis Registry Question it answers Mechanism
A. Processing engine FLOW_REGISTRY (flow-engine/registry.ts) Does extraction/company-lookup/validation/rematch run on the shared runFlow engine, or on a bespoke hand-written processor? v1-fork vs v2-generic
B. Review-screen projection REVIEW_REGISTRY (review/review-registry.ts) Does GET /api/submissions/:id/review know how to project this type into the generic ReviewData contract for the shared React review page? strangler: null (legacy bespoke page) vs a registered projector
C. Real-world validation none (informal / ClickUp / Efran's own testing) Has Efran actually driven this flow on a real submission end-to-end? smoke-tested vs never-driven

The review-registry.ts header states this explicitly: "review migration is orthogonal to processing migration — PENDIRIAN_PP migrates its review here while staying a v1-fork processor. When a flow is fully v2 end-to-end the two registries can merge; until then this map answers exactly one question."

Full ledger — all three axes, every flow

Flow SubmissionType A. Processing B. Review projection C. Smoke-tested
Pendirian PT PENDIRIAN_PT v1-fork (submission-processor.ts) ✅ migrated, flipped — Efran-approved, legacy page + old /submit route retired (2026-07-08) ✅ yes
Perubahan PT PERUBAHAN_PT v1-fork (perubahan-processor.ts) — shadow config exists ❌ not started — bespoke PerubahanDeltaReviewPage.tsx still serves it ✅ yes
Akuisisi (pengambilalihan) AKUISISI_PT v1-fork (akuisisi-processor.ts, rides perubahan extractor) — shadow config exists ❌ not started — bespoke AkuisisiDeltaReviewPage.tsx still serves it not smoke-tested (Efran-confirmed exception, 2026-07-09)
Perbaikan Data PT PERBAIKAN_DATA_PT v1-fork (perbaikan-processor.ts) ❌ not started — bespoke PerbaikanReviewPage.tsx ✅ yes
Peleburan (konsolidasi) PELEBURAN_PT v2-generic ✅ (flows/peleburan-pt.ts) not in REVIEW_REGISTRY — has its own bespoke pre-strangler page/route (PeleburanReviewPage.tsx/api/peleburan/submissions/:id/review-data), never folded into the generic strangler not smoke-tested (Efran-confirmed exception)
Laporan RUPS Tahunan LAPORAN_RUPS_TAHUNAN v2-generic ✅ (flows/laporan-rups-tahunan.ts) not in REVIEW_REGISTRY — same pattern, own bespoke page/route (LaporanRupsReviewPage.tsx/api/laporan-rups/submissions/:id/review-data) not smoke-tested (Efran-confirmed exception)
Pendirian PP PENDIRIAN_PP v1-fork (pp-pendirian-processor.ts) ✅ migrated, flipped — Efran-approved (4 waves), legacy retired (2026-07-04) ✅ yes
Pembubaran PP PEMBUBARAN_PP v1-fork (inline; no reprocess entrypoint) ✅ migrated, flipped — Efran-approved (2 waves), legacy retired (2026-07-06) ✅ yes
Peralihan PP→PT PERALIHAN_PP_KE_PT v1-fork (pp-peralihan-pt.ts) Efran-approved 2026-07-09 — flip landing in a separate concurrent session (projector + V2 page built 2026-07-08; canonical route swap + legacy page//review-data retirement in flight elsewhere) ✅ yes
Perubahan PP PERUBAHAN_PP v1-fork (pp-perubahan-processor.ts) ❌ not started — next up per Phase B roadmap, now highest overall priority (§0) ✅ yes
Perbaikan Data PP PERBAIKAN_DATA_PP v1-fork (pp-perbaikan-pp-processor.ts) ❌ not started — next up, paired with Perubahan PP ✅ yes
Apostille / Legalisasi APOSTILLE v1-fork, 2-phase (apostille-processor.ts) — missing "jalur prioritas" (priority-lane) flow/logic, flagged by Efran 2026-07-09, explicitly out of scope for now (tracked, not blocking) ❌ not started ✅ yes
Pembubaran PT PEMBUBARAN_PT stub (dark) here / v2-generic on the parallel feat/pt-flows-registry session n/a (flow doesn't run here yet) n/a
Berakhirnya Status PT BERAKHIRNYA_STATUS_PT enum absent here / v2-generic on the parallel session n/a n/a
Penggabungan (merger) — (enum missing) confirmed real SABH transaction — unbuilt everywhere. Classifier-recognized only (AKTA_TXN_TYPES since 2026-07-09); no SubmissionType, no flow, no fork, on either branch. NOW ON THE ROADMAP (§2.1) n/a n/a
~~Pemisahan (spin-off)~~ confirmed NOT a real SABH transaction type — dropped from consideration entirely (§2.1) n/a n/a

2.1 SABH re-confirmation (2026-07-09): what's actually real

Checked directly against ahu-online/protected/modules/sabhTransaksiPerseroan::getJenisTransaksi($id_aksi) is the canonical id→label map for Perseroan (PT) id_aksi_transaksi values, cross-checked against migrations:

id_aksi_transaksi Label (SABH) Our flow Status
9 Pendirian PENDIRIAN_PT built
10 Perubahan (/ "Penyesuaian" subcase) PERUBAHAN_PT built
11 Penggabungan confirmed real, unbuilt everywhere → roadmap item
12 Pembubaran PEMBUBARAN_PT confirmed real — v2-generic on the parallel session, dark stub here → port
19 Pengambilalihan (Akuisisi) AKUISISI_PT built (v1-fork)
35 Peleburan PELEBURAN_PT built (v2-generic)
36 Permohonan Berakhirnya Status Badan Hukum BERAKHIRNYA_STATUS_PT confirmed real — added to SABH via migration 20250808_add_aksi_transaksi_permohonan_berakhir_status.sql; v2-generic on the parallel session, enum absent here → port

No id_aksi_transaksi for Pemisahan exists anywhere in SABH — checked getJenisTransaksi's full id range plus a broad grep for stray id_aksi_transaksi literals across ahu-online; every other id found (2,3,6,7,8,14,15,21,25,28,29) belongs to Yayasan/Perkumpulan transactions or unrelated "beli profil" services, none to Perseroan spin-off. Conclusion: Pemisahan is not a real transaction — remove it from scope permanently, not just "defer."

PERBAIKAN_DATA_PT/PERBAIKAN_DATA_PP are confirmed as their own separate SABH module (views/perbaikanData/, views/verifikasiPerbaikanData/) — an administrative correction workflow, not an id_aksi_transaksi at all. No change needed; already correctly modeled as its own flow.

Important reframing on Pembubaran PT / Berakhirnya Status PT

This isn't a one-way "steal from legacy" — tidyup/progress.md records that the feat/pt-flows-registry work is a parallel session on a convergent, independently-arrived-at design (same Record<SubmissionType, FlowEntry> shape, same v1-fork/v2-generic split). The recorded plan is to RECONCILE at merge, never build a second registry: read their landed flow-engine/** as a second implementation of the same contract, reconcile the two, and land one registry — not blindly port-and-overwrite.


3. Review-projection mechanism (Axis B — already running, separate track)

This axis has its own strangler pattern, already proven on 4 flipped/flipping tenants (Pendirian PP, Pembubaran PP, Pendirian PT, and — Efran-approved 2026-07-09 — Peralihan PP→PT, whose flip is landing in a separate concurrent session). It does not touch the processing engine (Axis A) at all — a flow can flip its review screen while its processor stays v1-fork.

flowchart LR A["1 · Write a projector
(ReviewProjection:
sections + projectReview)"] B["2 · Register in
REVIEW_REGISTRY
(compiler-forced, per type)"] C["3 · Build the V2 page
(useReviewEngineData →
generic /api/.../review)"] D["4 · Efran smoke-tests
the V2 page"] E["5 · Flip canonical route
+ retire legacy page
+ legacy /review-data"] A --> B --> C --> D --> E D -.->|"not yet approved"| STOP["stays on legacy
bespoke page"]
  1. Projector — a pure projectReview(submissionId) → ReviewData | null that reads whatever the existing processor (v1-fork or v2-generic — doesn't matter) already wrote to the DB, and reshapes it into the one normalized ReviewData contract. Optional prepare() for pre-projection side effects (e.g. promoting deferred SKIPPED validations).
  2. Register — add to REVIEW_REGISTRY: Record<SubmissionType, ReviewProjection | null>. Adding a SubmissionType to the enum makes an unregistered entry a compile error (must be explicit null).
  3. V2 page — a thin page using the shared useReviewEngineData hook against GET /api/submissions/:id/review; contract-validated against ReviewDataSchema in non-prod.
  4. Efran smoke-tests the V2 page on real submissions — this is a hard gate, not a formality (Peralihan PP→PT is sitting here right now).
  5. Flip — canonical route renders the V2 page, /review-v2 becomes a redirect (keeps bookmarks alive), legacy hand-built page + its dedicated /review-data endpoint are deleted.

Where Peleburan and Laporan RUPS sit: they predate this strangler pattern entirely. They already have their own bespoke ReviewData-shaped page + dedicated route (/api/peleburan/submissions/:id/review-data, /api/laporan-rups/submissions/:id/review-data) built directly alongside their v2-generic processing flow — never registered in REVIEW_REGISTRY, never strangled. Folding them in is optional cleanup, not required for Axis A work.

Practical implication for Axis A (processing) migration: when PENDIRIAN_PT, PERUBAHAN_PT, AKUISISI_PT, etc. move their processor to v2-generic, their review screens don't need to change in lockstep — Axis B only cares that the DB rows the projector reads still exist in the same shape. Sequence them independently; just re-verify each projector still parses correctly after an Axis A flip.


4. Processing-engine migration mechanism (Axis A, the reusable procedure)

The tidy foundation already ships the migration harness — __shadow__ configs + an equivalence oracle (flow-engine/__tests__/flow-parity.test.ts). The per-flow procedure:

flowchart LR A["1 · Write shadow
FlowConfig
(defineFlow, NOT registered)"] B["2 · Parity oracle
fork vs shadow on
cloned fixture →
deep-equal results"] C["3 · Flip registry
v1-fork → config
+ BUILT_AKTA_TXN_TYPES"] D["4 · Delete bespoke
processor +
collapse fat route"] E["5 · Smoke test
real submission
end-to-end"] A --> B --> C --> D --> E B -.->|"RED = plumbing bug,
fix engine not validator"| A
  1. Shadow config — a defineFlow<Ctx>({ type, engine:'v2-generic', … }) that reuses the fork's own rule bodies and context. Precedent: flows/__shadow__/perubahan-pt.shadow.ts, akuisisi-pt.shadow.ts (TESTS-ONLY, not in FLOW_REGISTRY).
  2. Parity oracle — seed two clones of one submission; run the fork on one and the shadow's buildContext → runValidations → persist on the other; deep-equal the persisted ValidationResult rows on {ruleCode, ruleLabel, status, message}. A red diff is an engine-plumbing bug, fixed in the engine, never by weakening the diff.
  3. Flip — swap the FLOW_REGISTRY entry from {engine:'v1-fork'} to the config; add the txn type to BUILT_AKTA_TXN_TYPES where classifier-routed. Dispatch already prefers v2-generic.
  4. Delete — remove the bespoke processor and fold its route into the generic route-family surface.
  5. Smoke test — mandatory; these are flows the team actually runs.

⚠️ The current oracle only covers the validation half (context-lift → runner → persist). It does not yet assert parity on extraction, company lookup, rematch, section shaping, or finalize. Before migrating a mature flow, the oracle must be extended to a full-pipeline parity harness, or migration risks silently regressing behaviour the validation-only diff can't see.


5. Sequenced waves (Axis A — processing)

Re-sequenced 2026-07-09 to match Efran's stated priority: PP flows → Apostille/Legalisasi flows → PT flows (§0). This inverts the PT-first draft below it in favor of business priority; Wave 0 (engine validation) still goes first regardless of family, since every later v2-generic migration depends on it being proven.

The project's own tidyup/tasks.md "Phase B" roadmap sequences Axis B (review) as: Perubahan PP + Perbaikan Data PP next, then "(post smoke-test) diff wave: Perubahan PT + Akuisisi PT; then Perbaikan PT; Apostille last" for Axis A. That Axis-B ordering already agrees PP comes before the PT diff wave; it predates the explicit Apostille-before-PT call and doesn't cover Penggabungan (undiscovered at the time).

flowchart TB W0["WAVE 0 · Validate the v2-generic engine
(prerequisite for every later wave)"] W1["WAVE 1 · PP family (priority #1)"] W2["WAVE 2 · Apostille / Legalisasi (priority #2)"] W3["WAVE 3 · PT family (priority #3)"] W0 --> W1 --> W2 --> W3 W0 --- w0a["smoke Peleburan + Laporan RUPS
(the only unproven v2-generic evidence)"] W1 --- w1a["Perubahan PP + Perbaikan Data PP
(review projection, next per Phase B)"] W1 --- w1b["PP context builder + PP rule sets
(processing engine prerequisite)"] W2 --- w2a["jalur-prioritas gap stays OUT of scope
(tracked, not blocking)"] W2 --- w2b["2-phase engine extension
(classify → gated extract)"] W3 --- w3a["port PEMBUBARAN_PT → BERAKHIRNYA_STATUS_PT
(reconcile parallel-session drift)"] W3 --- w3b["PERUBAHAN_PT · AKUISISI_PT
(shadows exist)"] W3 --- w3c["PENDIRIAN_PT · PERBAIKAN_DATA_PT"] W3 --- w3d["build PENGGABUNGAN
(new — no fork anywhere to migrate from)"]

Wave 0 — validate the engine (prerequisite, do regardless of family — lowest risk, highest unblocking)

  • 0a. Smoke Peleburan + Laporan RUPS. No code; proves the v2-generic engine works on a real submission before anything else rides it. This is now the only remaining unproven piece of the engine — everything else Efran has already exercised.
  • 0b. Extend the parity oracle to full-pipeline (see §6) before any Wave 3 processing migration relies on it.

Wave 1 — PP family (priority #1)

  • Axis B (review): PERUBAHAN_PP + PERBAIKAN_DATA_PP — next per the existing Phase B roadmap, paired together (shares the FF4-grandfathered fork pair).
  • Axis A (processing): PENDIRIAN_PP, PERUBAHAN_PP, PERBAIKAN_DATA_PP, PEMBUBARAN_PP, PERALIHAN_PP_KE_PT all stay v1-fork today. Larger lift than PT: the generic engine's context is PT-akta-shaped (buildPtAktaContext); PP needs its own context builder + PP rule sets before any shadow config is possible. PEMBUBARAN_PP also lacks a standalone reprocess entrypoint (processes inline in its route) — needs one to fit the dispatch model.
  • Peralihan PP→PT's review flip is already in flight in a separate session (Axis B done); its processing (Axis A) stays v1-fork, same PP-context blocker as the rest of the family.

Wave 2 — Apostille / Legalisasi (priority #2)

  • Two-phase (classify → gated extract). The generic engine's single primaryAkta model doesn't express this; requires an engine extension (multi-phase / gated extraction) before it can become a v2-generic tenant.
  • Known gap, explicitly out of scope for now: missing "jalur prioritas" (priority-lane) flow/logic — flagged by Efran 2026-07-09. Track it; don't block the rest of the Apostille migration on it.
  • New rebuild codebase exists at ahu-rebuild/ahu-rebuild-apostille (ahu-apostille-api + module-apostille) — check there before assuming the legacy ahu-apostille repo is still the source of truth for future Apostille work.

Wave 3 — PT family (priority #3 — was previously framed as first; now last)

  • 3a. Port PEMBUBARAN_PTBERAKHIRNYA_STATUS_PT. Both confirmed real SABH transactions (§2.1), already v2-generic on the parallel feat/pt-flows-registry session. Add the BERAKHIRNYA_STATUS_PT enum member first (absent in tidy). Work = interface reconciliation, not a fresh build — feat/pt-flows-registry is 284 commits behind tidy's types.ts (focusedPasses, allowedStatusTransaksi, failGate, rematch.rupsAttendance all postdate it). Reconcile per §2's "convergent parallel session" note — merge, don't overwrite. Berakhirnya depends on Pembubaran landing first (its BSBHP_REGISTRY_STATE prerequisite gate reads an approved-pembubaran registry snapshot; shares the 6-PDF likuidasi package).
  • 3b. PERUBAHAN_PT, AKUISISI_PT. Shadows already exist and pass validation parity. Remaining: extend parity to full pipeline (blocked on Wave 0's oracle work), flip registry, delete perubahan-processor.ts / akuisisi-processor.ts, collapse the 1763-line perubahan route, smoke (Akuisisi has never been smoke-tested — do this before, not after, flipping it). Akuisisi keeps its koran-window rule + forcePeralihanOn hook (already modelled in the shadow).
  • 3c. PENDIRIAN_PT, PERBAIKAN_DATA_PT. No shadow yet; build shadow → parity → flip → delete submission-processor.ts / perbaikan-processor.ts → smoke.
  • 3d. Build PENGGABUNGAN (merger) — genuinely new, not a migration. Confirmed real (id_aksi_transaksi=11, §2.1); classifier already recognizes it but no branch has a SubmissionType, context, rules, or route for it. No fork to shadow — this is fresh v2-generic work from scratch, likely modeled on Peleburan's multi/single-entity precedent (a merger absorbs one+ PTs into a surviving PT, structurally closer to Peleburan's consolidation shape than to a diff-based Perubahan). Lowest-confidence estimate in this whole plan since there's no donor implementation anywhere to reconcile against.

6. Known engine gaps to close during migration

Gap Impact Fix
Parity oracle = validation-only Migrating a mature flow can regress extraction/lookup/rematch/sections undetected Extend to full-pipeline parity before Wave 3's PT migrations
Multi-source lookup is PELEBURAN-COUPLED runFlow hardcodes import('../services/peleburan-sources'); a 2nd multi-source flow would run peleburan's resolver Introduce a per-flow companyLookup.resolve hook (noted inline in processor.ts)
Interface drift on legacy ports feat/pt-flows-registry is 284 commits behind; its defineFlow/context/rules signatures are older Reconcile each ported file against current types.ts during Wave 3a
PP context absent from engine Generic context = PT-akta only Build PP context builder + rule sets (Wave 1 prerequisite — now highest priority)
Apostille 2-phase not expressible Single primaryAkta extraction model Engine extension for gated/multi-phase extraction (Wave 2)
Fat routes not yet absorbed perubahan/pp routes are 1500–1800 lines of bespoke handlers Generic route-family must cover this surface before deleting them
No donor for Penggabungan Unlike every other Wave 3 item, there's no fork or parallel-session implementation to shadow/reconcile against Budget it as new design work, not a port (Wave 3d)

7. Recommended order of operations

  1. Smoke Peleburan + Laporan RUPS (engine proof — do this first, it's free; the only unproven v2-generic evidence left).
  2. PP family (priority #1): build the PP context builder + rule sets, land Axis B for Perubahan PP/Perbaikan Data PP, then work Axis A.
  3. Apostille family (priority #2): design the engine's 2-phase extension; leave "jalur prioritas" tracked but out of scope.
  4. PT family (priority #3): port PEMBUBARAN_PTBERAKHIRNYA_STATUS_PT (reconcile, don't overwrite), extend the parity oracle to full-pipeline, migrate PERUBAHAN_PT + AKUISISI_PT (smoke Akuisisi before flipping it), then PENDIRIAN_PT + PERBAIKAN_DATA_PT, then build PENGGABUNGAN fresh.

Permanently out of scope: PEMISAHAN — confirmed not a real SABH transaction type (§2.1).