-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsample.html
More file actions
24 lines (23 loc) · 8.39 KB
/
Copy pathsample.html
File metadata and controls
24 lines (23 loc) · 8.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Sample Diagnostic — FlowPatch</title><style>
:root{--ink:#10212b;--muted:#52636d;--paper:#f6f8f7;--card:#fff;--brand:#087e6b;--accent:#d9f5ec;--line:#dce5e2;--warn:#fff2cf;--risk:#ffe0dc}*{box-sizing:border-box}body{margin:0;background:var(--paper);color:var(--ink);font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif}main{max-width:940px;margin:auto;padding:28px}a{color:var(--brand)}header{padding:50px 0 34px}.tag{display:inline-block;background:var(--accent);color:#075d50;padding:6px 12px;border-radius:999px;font-weight:800;font-size:13px}h1{font-size:clamp(36px,6vw,62px);line-height:1.03;letter-spacing:-.04em;margin:17px 0}h2{font-size:29px;line-height:1.2;margin:0 0 16px}h3{margin:0 0 7px}.lead{font-size:20px;color:var(--muted);max-width:760px}section{padding:36px 0;border-top:1px solid var(--line)}.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:22px;margin:14px 0}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:15px}.risk-high{border-left:7px solid #d54838}.risk-med{border-left:7px solid #e8a622}.risk-low{border-left:7px solid #2c9d7e}.pill{font-size:12px;font-weight:850;letter-spacing:.04em;padding:3px 8px;border-radius:99px;background:#edf1ef}.state{font-family:ui-monospace,SFMono-Regular,monospace;background:#10212b;color:#e9fbf6;border-radius:14px;padding:22px;overflow:auto}.rule{padding:14px 0;border-bottom:1px solid var(--line)}.rule:last-child{border:0}table{width:100%;border-collapse:collapse;background:#fff;border-radius:12px;overflow:hidden}th,td{text-align:left;vertical-align:top;padding:13px;border-bottom:1px solid var(--line)}th{background:#eaf2ef}.cta{display:inline-block;background:var(--brand);color:#fff;text-decoration:none;font-weight:800;padding:13px 19px;border-radius:9px;margin-top:10px}.note{background:var(--warn);border-radius:14px;padding:18px}.muted{color:var(--muted)}footer{padding:35px 0;color:var(--muted);font-size:14px}@media(max-width:650px){main{padding:18px}table{font-size:14px}}
</style></head><body><main>
<header><a href="./">← FlowPatch</a><br><br><span class="tag">ILLUSTRATIVE SAMPLE · NOT CLIENT DATA</span><h1>Async image-provider boundary diagnostic</h1><p class="lead">Example of the specificity in a $99 diagnostic. Scenario: an agency app submits image jobs to a third-party provider, polls status, receives webhooks, and stores final assets.</p></header>
<section><h2>Executive finding</h2><div class="card risk-high"><h3>The boundary can create duplicate jobs and lose completed assets.</h3><p>The current design treats a timeout as a failed request even when the provider may have accepted it. Polling and webhooks can then race, while retries have no stable idempotency key. The corrective design needs one local operation record, deterministic request identity, and monotonic state transitions.</p></div></section>
<section><h2>Risk matrix</h2><div class="grid"><div class="card risk-high"><span class="pill">HIGH</span><h3>Submit timeout</h3><p><strong>Failure:</strong> client times out after provider accepts the job.</p><p><strong>Impact:</strong> blind retry creates duplicate paid work.</p><p><strong>Control:</strong> deterministic idempotency key plus reconciliation before resubmit.</p></div><div class="card risk-high"><span class="pill">HIGH</span><h3>Webhook/poller race</h3><p><strong>Failure:</strong> both paths finalize the same job.</p><p><strong>Impact:</strong> duplicate downstream publication or billing.</p><p><strong>Control:</strong> compare-and-set finalization with a unique effect record.</p></div><div class="card risk-med"><span class="pill">MEDIUM</span><h3>Late success</h3><p><strong>Failure:</strong> local timeout marks a job failed before a delayed success webhook.</p><p><strong>Impact:</strong> completed asset is orphaned.</p><p><strong>Control:</strong> separate terminal provider state from operator-visible retry eligibility.</p></div><div class="card risk-low"><span class="pill">LOW</span><h3>Unknown webhook</h3><p><strong>Failure:</strong> valid event arrives before submit response is stored.</p><p><strong>Impact:</strong> temporary unmatched event.</p><p><strong>Control:</strong> persist and replay unmatched events for a bounded window.</p></div></div></section>
<section><h2>Required state model</h2><div class="state">created
→ submit_pending
→ submitted(provider_job_id)
→ running
→ succeeded(asset_ref) ─→ finalized(effect_id)
↘ submit_unknown ─→ reconciling ─→ submitted | retry_eligible
↘ failed_retryable ─→ retry_pending
↘ failed_terminal
Invariant: finalized may be entered once only.
Invariant: a known provider_job_id is never resubmitted.</div></section>
<section><h2>Concrete retry and reconciliation rules</h2><div class="card"><div class="rule"><strong>R1 — Identity.</strong> Generate <code>operation_id</code> before the first network call. Derive the provider idempotency key from that stable value; never from attempt number or timestamp.</div><div class="rule"><strong>R2 — Ambiguous submit.</strong> On connection reset or timeout, move to <code>submit_unknown</code>. Query by idempotency key or provider job reference before another POST.</div><div class="rule"><strong>R3 — Backoff.</strong> Retry only documented transient classes: 408, 429, and selected 5xx. Use exponential backoff with jitter and a bounded attempt/time budget. Honor <code>Retry-After</code>.</div><div class="rule"><strong>R4 — Finalization.</strong> Poller and webhook call the same idempotent transition. Store a unique <code>(operation_id, effect_type)</code> record before publishing, charging, or notifying.</div><div class="rule"><strong>R5 — Monotonicity.</strong> A late <code>running</code> event cannot overwrite <code>succeeded</code>. A late success may reconcile a locally timed-out operation unless a human explicitly cancelled it.</div><div class="rule"><strong>R6 — Dead letter.</strong> Events that fail validation or matching are persisted with reason, payload hash, first-seen time, and replay status.</div></div></section>
<section><h2>Failure-path test checklist</h2><table><tr><th>Injected condition</th><th>Expected assertion</th></tr><tr><td>Provider accepts POST; response times out</td><td>Exactly one provider job after reconciliation and retry.</td></tr><tr><td>Duplicate webhook delivered five times</td><td>One final state transition and one downstream effect.</td></tr><tr><td>Poll returns success while webhook handler runs</td><td>One compare-and-set winner; loser becomes a no-op.</td></tr><tr><td>429 with Retry-After</td><td>No retry before the server-defined delay; attempt budget remains bounded.</td></tr><tr><td>Success arrives after local timeout</td><td>Asset is attached; state does not remain terminal-failed.</td></tr><tr><td>Malformed signature / unknown operation</td><td>No state mutation; event stored with an actionable reason.</td></tr></table></section>
<section><h2>Patch plan and estimate</h2><div class="grid"><div class="card"><h3>1. Persistence & identity</h3><p>Operation table, unique constraints, deterministic idempotency key.</p><strong>Estimate: 2–3 hours</strong></div><div class="card"><h3>2. Transition service</h3><p>Shared compare-and-set path for poll and webhook handlers.</p><strong>Estimate: 3–5 hours</strong></div><div class="card"><h3>3. Reconciliation worker</h3><p>Bounded handling for ambiguous submits and unmatched events.</p><strong>Estimate: 2–4 hours</strong></div><div class="card"><h3>4. Failure-path tests</h3><p>Six deterministic tests with provider responses stubbed.</p><strong>Estimate: 2–3 hours</strong></div></div><div class="note"><strong>Total illustrative estimate: 9–15 engineering hours.</strong> A real diagnostic would name the exact files, database constraints, provider endpoints, and error classes after reviewing the accepted boundary.</div></section>
<section><h2>Want this level of specificity for your workflow?</h2><p>The fixed-fee diagnostic is $99 for one provider boundary and arrives within two business days after written scope acceptance and confirmed payment.</p><a class="cta" href="mailto:flowpatchpilot@proton.me?subject=FlowPatch%20diagnostic&body=Provider%20boundary%3A%0AObserved%20failure%3A%0AStack%3A">Request a diagnostic</a></section>
<footer>FlowPatch · flowpatchpilot@proton.me · <a href="./">Return to the main offer</a></footer>
</main></body></html>