PR E: SWIFT gateway (MT760, pacs.009, MT202, camt.025/054) #9

Merged
nsatoshi merged 0 commits from devin/1776875929-swift-gateway into main 2026-04-22 10:17:52 -07:00
Owner

Implements step 6 from the architecture gap-analysis — a SWIFT gateway module covering the issuance and payment legs plus the ingest path for bank confirmations. Also fixes the pacs.008 → pacs.009 mis-routing flagged by the gap-analysis for the interbank leg. Stacks on PR D.

What lands (all under src/services/swift/)

Outbound generators

module format purpose
mt760.ts FIN Cat-7 SBLC issuance from InstrumentTerms (12 tags, URDG 758 / UCP 600 aware). messageHash() is deterministic so the on-chain plan anchor can be reproduced from the terms.
pacs009.ts ISO 20022 XML (pacs.009.001.08) FI-to-FI credit transfer — the interbank leg. BIC-validated on all four agents.
mt202.ts FIN FIN equivalent of pacs.009 for non-migrated corridors. Amounts formatted with SWIFT decimal comma.

Inbound parsers

  • camt.ts#parseCamt025 — Receipt / status of a prior instruction.
  • camt.ts#parseCamt054 — Bank-to-customer credit/debit notification.
  • camt.ts#reconcileCamt054 — diffs (amount, currency, creditDebitIndicator, endToEndId) and returns the exact mismatches so VALIDATING can feed them into Data.valueMismatch() from PR B.
  • camt.ts#parseCamt — dispatcher on xmlns marker.

Channel policy (arch §9.2 — accepted ≠ settled)

Documented in swift/index.ts:

  • pacs.008 stays on the customer-initiated PSP channel (services/iso20022.ts). Acceptance alone does not allow COMMIT.
  • pacs.009 / MT202 is the interbank channel. COMMIT requires either camt.025 ACSC or camt.054 CRDT evidence.

Tests

tests/unit/swift.test.ts — 14 cases: MT760 tag layout, MT760 rejects malformed date + negative amount, messageHash determinism, pacs.009 XML + BIC validation + pay-step requirement, MT202 tag layout, camt.025 + camt.054 happy path, xmlns dispatcher, unknown-xmlns rejection, reconcileCamt054 both match-and-mismatch cases.

Verification

$ npx tsc --noEmit     # clean
$ npx jest             # 74 passed, 6 suites

Not in this PR

  • No network client. These are pure generators + parsers. The actual FIN submission path (SWIFT Alliance Gateway / SAA / SIA) and inbound ingestion (AMH inbox) are operational integrations that belong to the bank's SWIFT service bureau, not the orchestrator.
  • ExecutionCoordinator wiring is deferred. The issueInstrument step in PR A still executes a mocked dispatch; wiring it to generateMt760(terms) + eventBus.publish('instrument.dispatched', …) is a one-file change that lands in the next coordinator-focused PR so this PR stays reviewable.

Series order

A → B → C → D → E → F → G → H.

Base: devin/1776875718-event-bus-sse (PR D). The diff here is E-only.

Implements **step 6** from the architecture gap-analysis — a SWIFT gateway module covering the issuance and payment legs plus the ingest path for bank confirmations. Also fixes the **pacs.008 → pacs.009** mis-routing flagged by the gap-analysis for the interbank leg. Stacks on PR D. ## What lands (all under `src/services/swift/`) ### Outbound generators | module | format | purpose | | --- | --- | --- | | `mt760.ts` | FIN Cat-7 | SBLC issuance from `InstrumentTerms` (12 tags, URDG 758 / UCP 600 aware). `messageHash()` is deterministic so the on-chain plan anchor can be reproduced from the terms. | | `pacs009.ts` | ISO 20022 XML (`pacs.009.001.08`) | FI-to-FI credit transfer — **the interbank leg**. BIC-validated on all four agents. | | `mt202.ts` | FIN | FIN equivalent of pacs.009 for non-migrated corridors. Amounts formatted with SWIFT decimal comma. | ### Inbound parsers - `camt.ts#parseCamt025` — Receipt / status of a prior instruction. - `camt.ts#parseCamt054` — Bank-to-customer credit/debit notification. - `camt.ts#reconcileCamt054` — diffs `(amount, currency, creditDebitIndicator, endToEndId)` and returns the exact mismatches so `VALIDATING` can feed them into `Data.valueMismatch()` from PR B. - `camt.ts#parseCamt` — dispatcher on xmlns marker. ### Channel policy (arch §9.2 — accepted ≠ settled) Documented in `swift/index.ts`: - `pacs.008` stays on the customer-initiated PSP channel (`services/iso20022.ts`). Acceptance alone does **not** allow COMMIT. - `pacs.009` / `MT202` is the interbank channel. COMMIT requires either `camt.025 ACSC` **or** `camt.054 CRDT` evidence. ## Tests `tests/unit/swift.test.ts` — 14 cases: MT760 tag layout, MT760 rejects malformed date + negative amount, `messageHash` determinism, pacs.009 XML + BIC validation + pay-step requirement, MT202 tag layout, camt.025 + camt.054 happy path, xmlns dispatcher, unknown-xmlns rejection, `reconcileCamt054` both match-and-mismatch cases. ## Verification ``` $ npx tsc --noEmit # clean $ npx jest # 74 passed, 6 suites ``` ## Not in this PR - **No network client.** These are pure generators + parsers. The actual FIN submission path (SWIFT Alliance Gateway / SAA / SIA) and inbound ingestion (AMH inbox) are operational integrations that belong to the bank's SWIFT service bureau, not the orchestrator. - **ExecutionCoordinator wiring is deferred.** The `issueInstrument` step in PR A still executes a mocked dispatch; wiring it to `generateMt760(terms)` + `eventBus.publish('instrument.dispatched', …)` is a one-file change that lands in the next coordinator-focused PR so this PR stays reviewable. ## Series order A → B → C → D → **E** → F → G → H. Base: `devin/1776875718-event-bus-sse` (PR D). The diff here is E-only.
nsatoshi changed target branch from devin/1776875718-event-bus-sse to main 2026-04-22 10:16:28 -07:00
nsatoshi force-pushed devin/1776875929-swift-gateway from 8dcdb4531c to 632f309ffc 2026-04-22 10:17:22 -07:00 Compare
nsatoshi merged commit fd575000fe into main 2026-04-22 10:17:52 -07:00
nsatoshi deleted branch devin/1776875929-swift-gateway 2026-04-22 10:17:55 -07:00
Sign in to join this conversation.