Flag Rollout: `matching_shipto_resolver_v1`
Owner: david@boonetek.com
Created: 2026-06-17
Default: false (disabled, fail-safe)
Policy: docs/feature-flag-policy-erp-writes.md — §1 “Customer/item matching → customer resolution”
Seed config: infrastructure/flagsmith/configs/flags.json
What it gates
Section titled “What it gates”The CK ship-to customer resolver — the resolve_customer_by_shipto activity
in apps/temporal-worker/activities/typesense.py. The resolver picks the
DC-granular WhereFour customer from (sender_domain + extracted ship-to) before
the name-based match_customer fallback runs.
This is the outer of two gates, both DEFAULT-OFF and both required:
- Outer (this flag):
matching_shipto_resolver_v1, evaluated per-org via Flagsmith identity = the bare Clerk org id (alreadyorg_-prefixed). Policy gate on the shared activity, so it covers both call sites —workflows/pdf_extraction.py(PR #1721) andworkflows/email_processing.py(PR #1734) — and the feature can never be half-enabled on one path. - Inner (unchanged): the connection’s
matchingConfig.customerResolution.enabledconfig flag + the CK org belt-and-suspenders guard (_is_resolver_enabled).
When either gate is off — or the resolver can’t confidently resolve — the
activity returns None and the caller falls back to today’s name-match path,
byte-for-byte.
Fail-safe
Section titled “Fail-safe”get_feature_flag(..., default=False) returns the default on any error or when
Flagsmith is unreachable, so an absent or unreachable flag resolves to OFF.
Landing this PR changes nothing in any environment until the flag is created and
enabled for a targeted org.
Staged rollout
Section titled “Staged rollout”Follows the three-stage protocol in the policy. Because the inner CK org guard still refuses any non-CK org, the realistic targets are below — the flag’s job is to be the auditable, instantly-reversible kill switch on top of that guard.
Stage 0 — Create the flag (before merge)
Section titled “Stage 0 — Create the flag (before merge)”- Create
matching_shipto_resolver_v1in the Flagsmith UI (staging and production environments), boolean, default OFF, owner + description per the entry ininfrastructure/flagsmith/configs/flags.json. - Leave it OFF everywhere. Merge is safe (fail-safe OFF).
Stage 1 — Internal (1–3 days)
Section titled “Stage 1 — Internal (1–3 days)”- Enable the flag for
org_internal_testonly. - Smoke-test the gate plumbing: with flag-ON-but-non-CK-org, confirm the resolver still abstains (inner CK guard holds) and orders flow through the name-match path unchanged.
- Gate: zero ERP submission failures in 24h; no errors in the resolver
logs (
shipto-resolver:lines).
Stage 2 — Canary customer (3–5 days)
Section titled “Stage 2 — Canary customer (3–5 days)”- Enable the flag for the CK org. The inner CK guard
(
CK_ORG_IDSinapps/temporal-worker/activities/typesense.py) originally only recognized the WhereFour Sandbox eval org (org_3DisstYyUfNutmFA68xw5hxXfq1, used for this canary stage) — it never covered CK’s real production org (org_3FVE59k9pBxf7fJBJKNCAPNEQaW), so the resolver could never actually protect real CK orders. Fixed as part of ENG-560; both org ids now pass the guard. Enabling the flag + the connection’scustomerResolution.enabledfor CK’s real WhereFour prod connection is still a separate, un-taken ops step. - Monitor resolver hit rate, resolved-vs-abstained ratio, and downstream ERP submission success on CK orders. Compare resolved customers against the name-match result for the same orders.
- Gate: no customer-reported issues in 72h; error rate within baseline; no wrong-customer binds attributable to the resolver.
Stage 3 — General availability
Section titled “Stage 3 — General availability”- Set the flag default to
true. The resolver remains effectively CK-only until the inner CK org guard and per-connection config are generalized in a follow-up — so GA here means “the outer policy gate is no longer the limiter.” - Continue monitoring for 1 week.
- Gate: error rate within ±5% of pre-change baseline.
Rollback
Section titled “Rollback”- Toggle the flag OFF in Flagsmith — instant, no deploy. The resolver abstains and every order falls back to the name-match path. This is exactly the flag-absent behavior, so rollback restores pre-change behavior fully.
- Escalate to OFF immediately if the error rate spikes >10% above baseline at any stage.
Monitoring
Section titled “Monitoring”Per the policy §5 table — for this flag specifically:
| Metric | Source | Alert threshold |
|---|---|---|
| ERP submission success rate | Temporal workflow completion | <95% (baseline ~99%) |
| Resolver errors / abstains | Cloud Run logs shipto-resolver: | New error pattern |
| Wrong-customer binds | CSR/support channel | Any within 24h of flag |
| Error rate (500s) | Cloud Run logs | >2x baseline |
Lifecycle
Section titled “Lifecycle”After 2 weeks at GA with no issues, the flag can be marked permanent and a cleanup ticket created to remove the flag check (and, separately, to revisit the CK org guard) within 30 days.