Door Comparison — what the offer costs, and what the store does

31 Aug 2026 · revised after review · supersedes the landed-cost draft · .claude/plans/2026-08-31-landed-cost-comparison.md

The constraint from the first draft is gone.

The exchange's decision-relevant facts — free shipping over $49, no sales tax collected, the Price Match Policy, on-post pickup — are properties of shopmyexchange.com, true for every brand it carries. They need no per-brand route. The AAFES column works now, with the honest gap being whether the Exchange stocks the brand, which the member checks. That moves the comparison from "blocked on exchange routes" to shippable in step 3.

The finding that moved the design

ID.me Shop is not a merchant — verified against a live page

shop.id.me/stores/506-columbia-sportswear has no cart and no checkout. It states the offer and links out to the brand's own site, and declares participation in "the Amazon Associates program and other affiliate programs."

So for all fifteen dual-door brands, the ID.me door and the brand-direct door land on the same merchant — identical shipping, ship time, tax treatment and price-adjustment policy. What differs is the percentage, the verification, and the exclusions. The original premise that ID.me shipping differs from brand-direct does not hold, and a comparison printing two freight figures for those doors would be inventing a distinction.

This is good news architecturally: freight and tax are properties of a store, not of an offer, and belong modelled once per store.

What was verified about the Exchange

FactStatus
Free standard shipping on orders $49.00 or moreCaptured · the $49.95 reported elsewhere is NEX's threshold, not a contradiction
Free standard shipping with the MILITARY STAR® Card, at any order totalConfirmed & corroborated
Free standard shipping on orders containing at least one MCSS itemCaptured verbatim
Free standard shipping to a deployed area (OIF/OEF, Bosnia, Kosovo)Captured verbatim
$4.95 standard shipping on other orders under $49Confirmed & corroborated
Oversized and freight items may incur additional fees · in-store pickup freeCaptured verbatim
"You do not pay sales tax on an Exchange Online Store purchase"Confirmed — nuance: Online Mall vendor items are taxed
Price Match Policy; excludes fine jewelry, closeouts, clearance, special orders, Mall concessionsConfirmed
Ship time 7–10 business days CONUS · up to 45 business days APO/FPOCorroborated

Captured, not transcribed — and it corrected a source

curl receives a 586,485-byte SPA shell containing none of the policy text, and returns the identical byte count for two different customer-service paths. Rendered in a real browser the page is fully server-populated. The AAFES page says $49.00; the $49.95 reported elsewhere is NEX's threshold. Both figures are real and belong to different channels.

The capture carries four facts a paraphrase dropped:

The three-level model

The ID.me finding forces a clean separation the first draft did not have. Each fact lives at the level it is actually true at, so it is stored once and cannot disagree with itself.

Dataset.channels[]
The store, when the channel is the store
AAFES, NEX, MCX, GovX · shipping · ship time · tax posture · price match · pickup. Curated, dated, cited, published.
Brand.merchantPolicy
The store, when the brand is the store
nike.com — reached through brandDirect and through idmeShop · shipping · ship time · price match · price adjustment.
Route.conditions
The offer
The percentage, the verifier, exclusions, minimum spend, membership requirement.

idmeShop carries a single brandHandoff mode, which is the ID.me finding encoded where it cannot be forgotten. The section below explains why one flag was not enough for the other channels.

GovX is two products, and the app already models them

The GovX page says it outright

"GOVX ID is accepted by thousands of online merchants. You also get access to GOVX.com, the largest ecommerce site for the Military and First Responder communities."

GOVX.com is a marketplace — cart, order status, returns, payment methods, Best Price Guarantee. Orders are processed by brand partners, ship times are noted per product, and return shipping is free. GOVX ID is a free verification product embedded on other merchants' sites, including a Shopify app.

So a brand appearing "both ways" sells on GOVX.com and gates a discount on its own site behind GOVX ID. In Basefox terms those are already two distinct routes:

The doorChannelVerifyRoute id
Buy it on GOVX.comgovxgovxID{brand}-govx
Buy on the brand's site, verified by GovXbrandDirectgovxID{brand}-brandDirect
Retracting a hazard I raised last round.

I warned that a brand carried on GovX both ways would generate one id for two routes — the same shape as the ID.me/brandDirect collision that already cost this project once. That rested on both doors living on the govx channel. They do not. The concrete case does not exist and the warning is withdrawn. The id-uniqueness gate check stays, demoted from hazard mitigation to cheap insurance: a few lines that would have caught the 2026 collision, and nothing else asserts the property.

What merchant: true/false still gets wrong

AAFES alone kills the boolean

The capture shows the Exchange Online Mall carved out of the MILITARY STAR offer, out of the tax exemption, and out of the Price Match Policy. One domain, two storefronts, three different rules between them. Fulfillment modes hold that; a boolean does not.

ChannelFulfillment modes
aafesonlineStore — four waivers, no tax, price match · onlineMall — taxed, no STAR waiver, no price match
nexonlineStore — captured; tax posture unread
cgxonlineStore — new channel, captured; tax posture unread
govxmarketplace — brand partners drop-ship, ship time per product, no expedited option, fee only at checkout, free returns
idmeShopbrandHandoff only — no cart, no checkout
mcxRetired. mymcx.com is marketing; its own copy reads "Shop MCX + NEX".

GovX captured — and the fee turns out not to exist publicly

Both policy articles are public; the earlier failures were the batch tool, not a permission or a login.

GovX publishes no shipping fee and no free-shipping threshold anywhere public. Neither policy page states one and a brand listing page carries only a footer link. This is a resolved unknown rather than an open one: the mode publishes calculatedAtCheckout: true, the same treatment AAFES's per-product Priority upcharge and CGX's USPS option get.

Returns become a comparison row

GovX pays return freight; AAFES refunds shipping only when the error is theirs. That is a real difference between two doors at the same price, and exactly the class of fact this feature exists to surface. FulfillmentMode gains returns, and the comparison card gains a row.

One policy shape across the exchanges, with CGX the outlier

A correction I made this round.

An earlier draft claimed AAFES and NEX differed fundamentally. That was an artifact of comparing the AAFES free-shipping page against the NEX combined shipping page. Reading the AAFES delivery-methods page shows the same policy shape: free above a threshold or with MILITARY STAR®, $4.95 standard otherwise, a faster paid tier above that.

AAFESNEXCGX
Free over$49.00$49.95 after discounts$150.00
MILITARY STAR®waiveswaiveswaives
Other waiversMCSS item · deployed area—uniform items
Standard$4.95$4.95banded $7 / $12 / $15
Faster tierupcharge per product pagePriority $12.95 · Express $17.95USPS at checkout
APO/FPOfree via deployed waiverup to 45 business daysnot stated
OCONUSno foreign civilian addressno street delivery; DHL to storesnot stated

The genuine variation is narrow: a 95-cent threshold gap, NEX alone stating its threshold is measured after discounts, and CGX as the real outlier with a band table and a $150 bar. Two exchanges price a tier they will not publish — AAFES's Priority upcharge sits on each product page and CGX's USPS rate is computed at checkout. Neither can ever be stated by Basefox, so the model must be able to say that rather than omit the method.

The roster changes, and gets smaller

MCX comes off. mymcx.com is a marketing site whose own copy reads "Shop MCX + NEX". This closes a standing inconsistency rather than creating one: the Swift ChannelKind carries an mcx case that neither the pipeline models nor the schema define, and DeepLink points it at that marketing page. No published route uses it, so retiring it is free today and will not be later.

CGX goes on. The Coast Guard Exchange is a real storefront with its own policy, absent from the app entirely.

Waivers, and who can apply them

A single freeOver: 49 field models the Exchange wrongly on three of its four paths

The policy waives freight four independent ways: order total, MILITARY STAR® Card, an MCSS item in the order, and a deployed destination. The model has to carry the set, with an autoApplies flag per waiver.

WaiverBasefox can apply it?
orderTotal $49.00Yes — the basket total is known
MILITARY STAR® CardYes — if the profile says the member holds one
deployed / APO FPOYes — derived from the ZIP already being loaded
MCSS item in orderNo — Basefox cannot see a cart. Stated, never subtracted.
in-store pickupNo — a member choice, not an order fact. Stated.

Two waivers are member attributes, and the ZIP work already half-built both

MILITARY STAR® holders ship free at any total. A large share of this audience carries the card, and for them the Exchange column changes at every basket size. Profile gains one toggle beside the status picker.

Deployed and APO/FPO addresses ship free — and the ZIP table loaded for sales tax already resolves prefixes 090–098, 340 and 962–966 to no state and therefore no tax. Those same prefixes now also mean no freight. A deployed member gets tax-free and freight-free at once, computed on data the app has to load anyway. Nothing else this population uses works that out for them.

Ship time is the counterweight, and it can reverse the answer

Standard shipping runs 7–10 business days CONUS and up to 45 business days to APO/FPO. A deployed member choosing between a free, tax-free Exchange order arriving in six weeks and a brand shipping in three days is making a trade no total expresses. Ship time is carried as its own row and never folded into the number — which is exactly why it was asked for.

Published policy, not compiled policy

registry/channels.yaml publishes into dataset.json as a top-level channels array rather than living in a Swift table. A shipping threshold hardcoded in a shipped binary cannot be corrected for however long review takes, and stating a wrong one with a confident face is the failure this app exists to prevent. Published, a sweep fixes it. Left out of the schema's required list, so the currently published dataset and the bundled snapshot both still validate.

An honest correction to the first draft

"Re-mine the 80 cached pages" is weaker than it looked

The cached texts in runs/latest/tier2-queue/ are discount and terms pages. Shipping policy, delivery estimates and price-adjustment windows live on different URLs — a shipping page, a returns page, a help centre. Some discount pages mention shipping in passing; most do not.

So the yield will be real but thin, and thinnest on exactly the fields named as decision variables. Correcting this now rather than letting step 4 discover it. The staging: re-mine what is cached (free), rescue the intake notes the gate currently discards (free), then add an optional shippingURL per brand and fetch the fifteen dual-door brands first. Where nothing is known the row reads not stated and the total is a floor — no inference, no assumed-free.

What the member sees

NIKE · BRAND DIRECT                    −30%
  discount     −30% via SheerID
  shipping     free over $50, else $6.95
  ship time    3–5 business days
  sales tax    collected · TX ~8.20%
  price match  no
  adjustment   within 14 days of purchase
  ✓ verified Aug 30 · Aug 30 sweep

NIKE · ID.ME SHOP                      −25%
  discount     −25% via ID.me
  ships to     nike.com — same store as above
  exclusions   sale items
  ✓ verified Aug 30 · Aug 30 sweep

AAFES · EXCHANGE                    varies
  discount     varies by item
  shipping     free — MILITARY STAR card
               (else free $49+, otherwise $4.95)
  ship time    7–10 business days
  sales tax    none collected
  price match  yes · Exchange Price Match
  pickup       on-post, free
  ⓘ also free with an MCSS item in the order
  ✓ policy checked Aug 31 · source
  ⓘ Basefox has no Exchange listing for Nike.
     Check on base or at shopmyexchange.com.

Two details fall out of the ID.me finding and matter:

The calculator

Default — one price. Type what the item costs; each door applies its own discount, then its own shipping and tax. Zero friction, and correct for the common case where two doors are the same merchant.

Override — prices differ by site. A toggle opens a per-door field. This is not polish: the Exchange does not apply a percentage, it has its own sticker price. Without per-door entry the AAFES column would assume the Exchange lists the same price as the brand — the exact assumption the comparison exists to remove.

RuleBehaviour
benefitKindpercentDiscount and fixedAmount compute. specialPricing and freeBenefit show as .notComputable and are excluded from re-ranking.
minimumSpendBelow the minimum the discount is not applied and a blocker attaches. A member entering $80 against a "20% off $150+" door currently reads −20% with nothing correcting them.
shippingEvery autoApplies waiver is evaluated against the discounted subtotal and the profile; the first that matches sets freight to zero and names itself. No match → flatUSD. Nothing stated at all → nil, and isFloor is true.
non-auto waiversAn MCSS or pickup waiver is shown and never subtracted. Basefox cannot see a cart, and quietly zeroing freight on a guess about its contents would be a fabricated total.
ship timeCarried and displayed, never folded into the number. A 45-business-day APO delivery can reverse the decision a total recommends.
taxMerchandise only, not freight. salesTax: none channels contribute zero.
membershipNever added. WeSalute has a price Basefox does not know; the blocker names the requirement without inventing a number.

isFloor is load-bearing

A store that never states shipping would otherwise produce the lowest total on the screen and look cheapest — the exact inversion this feature exists to fix. A floor renders $86.56 +shipping and sorts on its floor with the marker visible.

Gate checks

Two new detectors, because this data creates two new failure modes

policy-uniformity — gate.py exists because on 2026-08-29 the ID.me extractor published 82 brands at an identical −40% and nothing objected, since every individual route was well-formed. A freshly-added extraction field is where that recurs.

stale-channel-policy — a channels row whose lastVerified is older than a threshold raises a concern. A published shipping threshold that silently rots is the specific risk taken on by publishing this data at all.

Both hold the publish for a human rather than failing it; --force says the human looked.

Sequencing

  1. Channel policy end to end. FulfillmentMode, ShippingWaiver, Shipping; schema; Swift model; staleness and route-id-uniqueness gate checks. AAFES, NEX and CGX captured; mcx retired and cgx added. All five surviving channels captured. The NEX/CGX tax posture is the one remaining gap.
  2. Sales tax and member attributes. salestax.yaml, generator, SalesTax.swift, Profile gaining zip, taxRateIsManual and hasMilitaryStarCard. The ZIP table also resolves apoFpoAddress.
  3. Comparison surface, channel-level only. Stacked cards on channel policy plus the tax rate. First visible slice, and it already answers the AAFES question — before any brand-level extraction exists.
  4. Merchant policy + extraction. MerchantPolicy on Brand, Shipping, Conditions.excludes, Extraction fields, INTAKE_FIELDS widening, re-mine, uniformity check.
  5. Targeted fetches. shippingURL per brand, fifteen dual-door brands first.
  6. GovX fulfillment mode. Teach the GovX source to record retail vs. handoff, and let a brand publish both with the suffixed id. Until this lands, GovX cards read fulfillment unknown and state no freight or tax.
  7. Calculator. One-price default, per-door override, LandedCost, optional-basket ranking.

Steps 1 and 2 are independent. Step 3 depends on both and is the first shippable slice.

Final review

What this gets right

Where it is weak

Adjustments worth considering

Confidence

90
▲ up from 89
Four of five channels are captured rather than guessed, and the churn of the last two rounds came from reading too few pages rather than from the model being unsound. The method table is barely more than a flat fee for AAFES and NEX and is the only shape that holds CGX, so it carries real weight rather than speculative generality. The roster is smaller and more accurate than when this started.

GovX turned out to need no new modelling at all: the existing channel/verify split already separates GOVX.com from GOVX ID, and the route-id hazard I raised last round is withdrawn. Two rounds of caution about GovX resolved into a capture showing the app was already right.

All five surviving channels are captured. GovX's missing fee is a resolved unknown rather than an open one: it is not published anywhere public, so calculatedAtCheckout is the correct and final answer rather than a placeholder.

The 10 points: the NEX and CGX tax posture is unread and must not be inferred from AAFES; channel policy needs a rendered browser to re-read, so an unattended sweep cannot repair rot it detects; brand-level merchant coverage stays unknown until step 5 and is the largest remaining content risk; the state tax average will be visibly wrong for some members; and Exchange stock is still unanswered.