31 Aug 2026 · revised after review · supersedes the landed-cost draft · .claude/plans/2026-08-31-landed-cost-comparison.md
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.
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.
| Fact | Status |
|---|---|
| Free standard shipping on orders $49.00 or more | Captured · the $49.95 reported elsewhere is NEX's threshold, not a contradiction |
| Free standard shipping with the MILITARY STAR® Card, at any order total | Confirmed & corroborated |
| Free standard shipping on orders containing at least one MCSS item | Captured verbatim |
| Free standard shipping to a deployed area (OIF/OEF, Bosnia, Kosovo) | Captured verbatim |
| $4.95 standard shipping on other orders under $49 | Confirmed & corroborated |
| Oversized and freight items may incur additional fees · in-store pickup free | Captured 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 concessions | Confirmed |
| Ship time 7–10 business days CONUS · up to 45 business days APO/FPO | Corroborated |
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 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.
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 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 door | Channel | Verify | Route id |
|---|---|---|---|
| Buy it on GOVX.com | govx | govxID | {brand}-govx |
| Buy on the brand's site, verified by GovX | brandDirect | govxID | {brand}-brandDirect |
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.
merchant: true/false still gets wrongThe 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.
| Channel | Fulfillment modes |
|---|---|
| aafes | onlineStore — four waivers, no tax, price match · onlineMall — taxed, no STAR waiver, no price match |
| nex | onlineStore — captured; tax posture unread |
| cgx | onlineStore — new channel, captured; tax posture unread |
| govx | marketplace — brand partners drop-ship, ship time per product, no expedited option, fee only at checkout, free returns |
| idmeShop | brandHandoff only — no cart, no checkout |
Retired. mymcx.com is marketing; its own copy reads "Shop MCX + NEX". |
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.
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.
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.
| AAFES | NEX | CGX | |
|---|---|---|---|
| Free over | $49.00 | $49.95 after discounts | $150.00 |
| MILITARY STAR® | waives | waives | waives |
| Other waivers | MCSS item · deployed area | — | uniform items |
| Standard | $4.95 | $4.95 | banded $7 / $12 / $15 |
| Faster tier | upcharge per product page | Priority $12.95 · Express $17.95 | USPS at checkout |
| APO/FPO | free via deployed waiver | up to 45 business days | not stated |
| OCONUS | no foreign civilian address | no street delivery; DHL to stores | not 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.
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.
freeOver: 49 field models the Exchange wrongly on three of its four pathsThe 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.
| Waiver | Basefox can apply it? |
|---|---|
| orderTotal $49.00 | Yes — the basket total is known |
| MILITARY STAR® Card | Yes — if the profile says the member holds one |
| deployed / APO FPO | Yes — derived from the ZIP already being loaded |
| MCSS item in order | No — Basefox cannot see a cart. Stated, never subtracted. |
| in-store pickup | No — a member choice, not an order fact. Stated. |
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.
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.
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.
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.
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:
policy checked Aug 31), distinct from a route's verified stamp.
A standing policy is a different kind of claim from a swept offer and must not borrow the
sweep's seal.free — MILITARY STAR
card teaches the member why, and it is the app showing its work: a member without the
card sees free $49+, otherwise $4.95 instead, and the two readings never
silently swap.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.
| Rule | Behaviour |
|---|---|
| benefitKind | percentDiscount and fixedAmount compute. specialPricing and freeBenefit show as .notComputable and are excluded from re-ranking. |
| minimumSpend | Below 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. |
| shipping | Every 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 waivers | An 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 time | Carried and displayed, never folded into the number. A 45-business-day APO delivery can reverse the decision a total recommends. |
| tax | Merchandise only, not freight. salesTax: none channels contribute zero. |
| membership | Never added. WeSalute has a price Basefox does not know; the blocker names the requirement without inventing a number. |
isFloor is load-bearingA 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.
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.
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.salestax.yaml, generator, SalesTax.swift, Profile gaining zip, taxRateIsManual and hasMilitaryStarCard. The ZIP table also resolves apoFpoAddress.MerchantPolicy on Brand, Shipping, Conditions.excludes, Extraction fields, INTAKE_FIELDS widening, re-mine, uniformity check.shippingURL per brand, fifteen dual-door brands first.fulfillment unknown and state no freight or tax.LandedCost, optional-basket ranking.Steps 1 and 2 are independent. Step 3 depends on both and is the first shippable slice.
not stated until targeted fetches run.curl gets an SPA shell; a rendered session gets the policy. Channel policy therefore cannot be re-read by an unattended sweep the way a brand page can, so the staleness check can only flag rot, never repair it. The strongest argument yet for the deferred scraped-drift option, now with a concrete blocker attached.shipping in diff._MATERIAL so a threshold moving $50 → $75 reaches the What's New feed. Held back because each field needs its own headline sentence or the feed emits something wrong.calculatedAtCheckout is the correct and final answer rather than a placeholder.