Consequences, connected.
Every affected guarantee was evaluated on all of its prerequisites.
Why this verdict
R_BROKEN_T1G_CHARGE_ONCE is broken via G_CHARGE_ONCE -> G_DEDUPE -> A_RETRY_WINDOWR_BROKEN_T1G_DEDUPE is broken via G_DEDUPE -> A_RETRY_WINDOWR_BROKEN_T1G_LEDGER_CONSISTENT is broken via G_LEDGER_CONSISTENT -> G_CHARGE_ONCE -> G_DEDUPE -> A_RETRY_WINDOWR_BROKEN_T1G_REFUND_MATCH is broken via G_REFUND_MATCH -> G_CHARGE_ONCE -> G_DEDUPE -> A_RETRY_WINDOW
What changed
| Fact | Main | Proposed | Control |
|---|---|---|---|
paylane.retry_window_hoursconfig/partners/paylane.yaml:4 |
24 | 72 | external |
How far it reaches
flowchart LR A_RETRY_WINDOW["A\_RETRY\_WINDOW"] --> G_DEDUPE["G\_DEDUPE"] G_CHARGE_ONCE -. "relies on" .-> ADR_015["ADR-015"] G_CHARGE_ONCE -. "relies on" .-> ADR_018["ADR-018"] G_CHARGE_ONCE -. promises .-> ADR_012["ADR-012"] G_CHARGE_ONCE["G\_CHARGE\_ONCE"] --> G_LEDGER_CONSISTENT["G\_LEDGER\_CONSISTENT"] G_CHARGE_ONCE["G\_CHARGE\_ONCE"] --> G_REFUND_MATCH["G\_REFUND\_MATCH"] G_DEDUPE -. "relies on" .-> ADR_012["ADR-012"] G_DEDUPE -. promises .-> ADR_007["ADR-007"] G_DEDUPE["G\_DEDUPE"] --> G_CHARGE_ONCE["G\_CHARGE\_ONCE"] G_LEDGER_CONSISTENT -. promises .-> ADR_018["ADR-018"] G_REFUND_MATCH -. "relies on" .-> ADR_018["ADR-018"] G_REFUND_MATCH -. promises .-> ADR_015["ADR-015"] classDef broken fill:#2a0f1a,stroke:#ff6b8b,color:#ffd3dd classDef unknown fill:#2a2310,stroke:#ffd27a,color:#fff0cc classDef holds fill:#0d2620,stroke:#7cf0c8,color:#d8fff2 class G_CHARGE_ONCE,G_DEDUPE,A_RETRY_WINDOW broken class G_DEDUPE,A_RETRY_WINDOW broken class G_LEDGER_CONSISTENT,G_CHARGE_ONCE,G_DEDUPE,A_RETRY_WINDOW broken class G_REFUND_MATCH,G_CHARGE_ONCE,G_DEDUPE,A_RETRY_WINDOW broken
Dependency graph text
flowchart LR A_RETRY_WINDOW["A\_RETRY\_WINDOW"] --> G_DEDUPE["G\_DEDUPE"] G_CHARGE_ONCE -. "relies on" .-> ADR_015["ADR-015"] G_CHARGE_ONCE -. "relies on" .-> ADR_018["ADR-018"] G_CHARGE_ONCE -. promises .-> ADR_012["ADR-012"] G_CHARGE_ONCE["G\_CHARGE\_ONCE"] --> G_LEDGER_CONSISTENT["G\_LEDGER\_CONSISTENT"] G_CHARGE_ONCE["G\_CHARGE\_ONCE"] --> G_REFUND_MATCH["G\_REFUND\_MATCH"] G_DEDUPE -. "relies on" .-> ADR_012["ADR-012"] G_DEDUPE -. promises .-> ADR_007["ADR-007"] G_DEDUPE["G\_DEDUPE"] --> G_CHARGE_ONCE["G\_CHARGE\_ONCE"] G_LEDGER_CONSISTENT -. promises .-> ADR_018["ADR-018"] G_REFUND_MATCH -. "relies on" .-> ADR_018["ADR-018"] G_REFUND_MATCH -. promises .-> ADR_015["ADR-015"]
| Guarantee | Status / baseline | Tier | Promised by | Relied on by | Exposed callers |
|---|---|---|---|---|---|
G_CHARGE_ONCE |
brokenMain: holds | 1 | ADR-012 | ADR-018, ADR-015 | checkoutservice, frontend, refundworker, webhookingress |
G_DEDUPE |
brokenMain: holds | 1 | ADR-007 | ADR-012 | |
G_LEDGER_CONSISTENT |
brokenMain: holds | 1 | ADR-018 | checkoutservice, frontend, refundworker, webhookingress | |
G_REFUND_MATCH |
brokenMain: holds | 1 | ADR-015 | ADR-018 |
Checked and not affected
ADR-016 relies on G_ACK_FAST, also provided by ADR-007. Its full prerequisite set still holds.
Who should review
ADR-007 · Anika Rao, Ravi Menon
Roles: promises. Unavailable: none.
- Jun Park: 1 recent authored sources
- Anika Rao: 0 recent authored sources
ADR-012 · Jun Park, Anika Rao
Roles: promises, relies_on. Unavailable: Lena Fischer, Marco Ruiz.
- Jun Park: 7 recent authored sources
- Anika Rao: 2 recent authored sources
ADR-015 · Anika Rao
Roles: promises, relies_on. Unavailable: none.
- Jun Park: 1 recent authored sources
- Anika Rao: 0 recent authored sources
ADR-018 · Ravi Menon
Roles: promises, relies_on. Unavailable: none.
- Jun Park: 7 recent authored sources
- Anika Rao: 2 recent authored sources
Remedies that survive verification
Only complete remedies may open a fix PR. Partial and rejected alternatives remain visible.
No verified remedy available for this assessment.
Assumption evidence
| Assumption | State | Predicate / attestation |
|---|---|---|
A_ACK_BEFORE_TIMEOUTWe acknowledge every webhook before Paylane gives up on the delivery |
holding | 2000 < 5000webhook.ack_timeout_ms / paylane.delivery_timeout_ms |
A_REDIS_TTLThe shared Redis cluster keeps dedupe records for the full dedupe window |
holding | 24 <= 48webhook.dedupe_ttl_hours / redis.max_ttl_hours |
A_RETRY_WINDOWPaylane stops retrying a webhook before our dedupe records expire |
violated | 72 <= 24paylane.retry_window_hours / webhook.dedupe_ttl_hours |
A_STABLE_EVENT_IDPaylane sends the same event_id on every retry of a webhook |
holding | Anika Rao · activeAttested: 2026-09-02 00:00 UTC |
The lexical gap
Fulltext search
Fulltext for paylane | retry | window finds ADR-007, ADR-010, ADR-011, ADR-016.
Guarantee graph
The guarantee graph additionally reaches ADR-012, ADR-015, ADR-018, whose decision bodies do not mention those terms.
Source citations
ADR-007adr/ADR-007.md:2 (local checkout)ADR-012adr/ADR-012.md:2 (local checkout)ADR-015adr/ADR-015.md:2 (local checkout)ADR-018adr/ADR-018.md:2 (local checkout)A_ACK_BEFORE_TIMEOUTadr/ADR-007.md:35 (local checkout)A_REDIS_TTLadr/ADR-007.md:24 (local checkout)A_RETRY_WINDOWadr/ADR-007.md:21 (local checkout)A_STABLE_EVENT_IDadr/ADR-007.md:27 (local checkout)G_CHARGE_ONCEadr/ADR-012.md:12 (local checkout)G_DEDUPEadr/ADR-007.md:12 (local checkout)G_LEDGER_CONSISTENTadr/ADR-018.md:12 (local checkout)G_REFUND_MATCHadr/ADR-015.md:12 (local checkout)paylane.retry_window_hoursconfig/partners/paylane.yaml:4 (local checkout)