Version History — The Forge

The Forge — Sparrow Expeditions

Document SE-VHIST · Live log · June 2026

Version History — The Forge

Every change to The Forge gets shipped with a public note. Bugs found, fixes shipped, what changed and why. When I get something wrong and reverse course, that's in here too — those reversals are the most useful entries on the page.

If you find something I missed or a bug I should know about, write to sal@sparrowexpeditions.com.

v137June 3, 2026

Spine What-If — cycle through the standard spine ladder + see which one would land you in window

  • New "Find your in-window spine" panel on Build Review (SpineCheck) and the Spine Calculator. Renders inline below the verdict when the user's selected spine is anything other than in-range. Diagnostic-only — never mutates the saved spine. A small Sal-voice nudge points the user at the Bow Setup tab to make a swap permanent.
  • A · Single-spine dropdown. Industry-standard ladder (250 / 300 / 340 / 400 / 500 / 600 / 700 / 800 / 900 / 1000) with a 44 px touch target. Live verdict chip updates on each pick — emerald IN RANGE, amber BORDERLINE, rose TOO STIFF or TOO WEAK. Default-selected is the closest in-window value at the user's current setup.
  • B · Expandable matrix. "Show all spines" accordion — desktop table or mobile vertical card stack — with every ladder rung scored at the user's effective draw weight. Current spine row is ember-tinted with a "current" badge.
  • Algorithm parity locked in. Matrix verdicts come from the same canonical recommendSpineFromInputs (v135.1 unified function) the main verdict uses. Build Review and What-If can never disagree at the same inputs. Vitest re-runs the direct call for every ladder rung and compares to the matrix row.
  • Helper copy when no standard spine fits. When every ladder rung lands out-of-window at an extreme setup, a rose-tinted note surfaces: "None of the standard spines (250–1000) land in window at your current effective draw weight. This often means an extremely heavy compound or a heavily front-loaded arrow build. Talk to a pro shop about a custom spine setup, or check your draw-length / bow-type inputs above."
  • 9 new Vitest tests on the matrix util (ladder shape, parity check, Jesse's PSE Mach 33 DS setup confirmation, all-rows-share-eff-DW, no-fit guard, default-suggested-spine fallback chain). 196/196 passing across the engine.
  • +7.7 KB bundle delta. Carries forward v136.0 cloud sync (still active) and v135.3 cross-origin guard. Methods page § 11 documents the panel.

Field-tested via Jesse's PSE Mach 33 DS / K2 Sawtooth 4mm setup before shipping. Spine 300 lands as IN RANGE at his effective 83.75 lb (range 250–300); 340 onward read as TOO WEAK past the 25-unit tolerance buffer. Real-world tune matches — he's optimal where the algorithm now says he should be.

v136.0June 3, 2026

Account sync · Phase 1 — sign in with your Shopify customer account, sync the Range Log across devices

  • Anonymous Forge stays the default. Sign-in adds cross-device replication of Range Log sessions — every existing feature works exactly the same without an account.
  • Sign-In CTA local to the Range Log header. Anonymous users see an ember "Sign in · sync your training data" button. Post-login redirect lands on the Range Log with a cyan chip showing the signed-in email.
  • Identity round-trip via postMessage host bridge. Storefront Liquid emits { customer.id, customer.email, hmac_sha256(...) } with a secret shared between the theme and a Cloudflare Worker. The iframe never sees the secret — only the precomputed hash.
  • Cloudflare Worker + D1 backend. Three endpoints: identity verify, session upsert, session pull. JSON-blob payload storage lets the v3 client schema evolve without server migrations.
  • HMAC validation on every request. Worker recomputes against the same secret. Mismatch → 401. Compromised Cloudflare or D1 still couldn't forge requests under a different customer without the secret.
  • One-time backfill prompt + cross-device replication + photos stay on device on Free tier. Tier-agnostic session count — no throttling on Free.
  • New /pages/privacy page documenting the contract.
  • 9 new Vitest tests + 185/185 passing.
v135.3June 3, 2026

CRITICAL BUG · fixedCross-origin parent.location read crashed the entire bundle

  • The bug. useIdentity.tsx read window.parent?.location?.search to detect the ?signedin=1 redirect param. The optional chaining caught window.parent being undefined but NOT the cross-origin SecurityError thrown when reading .location on a different-origin Window. The Forge iframe lives on cdn.shopify.com; the storefront is sparrowexpeditions.com. Every page load threw — took the entire Forge down.
  • The amplifier. Shopify's CDN serves theme assets with content-security-policy: sandbox excluding allow-same-origin — the iframe gets a null origin. iOS Safari's variant of the cross-origin error message reads as "the frame is sandboxed and lacks 'allow-same-origin'" which sent us looking for a sandbox attribute we hadn't added. The real cause was the cross-origin policy itself.
  • The fix. Wrapped the parent.location read in try/catch with a fallback to window.location?.search (the iframe's own URL — Shopify forwards the query through). Audited identityBridge.ts + useCloudSync.ts for the same pattern — both clean (postMessage only, no direct parent property reads).
  • The recovery. Both v135.1 AND v135.2 carried this same bug from the v136.0 source merge. With no clean theme to roll back to, v135.3 patched + re-shipped end-to-end in under 4 minutes.

Lesson locked in memory: cross-origin parent access in a Shopify iframe always throws SecurityError. Optional chaining doesn't help — wrap with try/catch or use postMessage exclusively.

v135.2June 3, 2026

Share button Messenger-hijack fix + cut-length guardrail

  • Share fix. iframe allow attribute now includes web-share. Without it, navigator.share() was blocked by Permissions Policy and iOS Safari fell through to the system intent — which on phones with FB Messenger installed routed all shares directly to Messenger. With web-share added, the native iOS share sheet appears as expected.
  • Download fix. downloadSvgAsPng now tries navigator.share({ files }) first (files-only payload), which on iOS opens the share sheet with "Save to Files" / "Save Image" as top options. Falls back to <a download> for desktop browsers.
  • Cut-length guardrail (Jesse's pro-tester feedback). Recommender auto-suggested cuts now clamp to (AMO draw − 1″) as a hard floor for rest and broadhead clearance. User overrides past the floor surface a caution-tier SafetyNote; past (AMO − 2.5″) or with fixed-blade broadheads selected, the note upgrades to critical.
  • 7 new Vitest tests on the cut-length verdict tiers. 185/185 passing.
v135.1June 3, 2026

BUG · fixedJesse's pro-tester report — Spine page DL reset, Build/Spine verdict divergence, AMO draw-length labeling

  • Issue A · Spine page reset draw length to 29″ every visit. SpineCalculatorView's useEffect re-synced shaft/point/insert/nock fields when activeSetup.id or activeArrowIdx changed — but NOT draw weight or draw length, and didn't depend on the active bow's values. The tab-stay-mounted layout meant local state never refreshed from external edits. Fix: extended dependency array to include activeBow?.drawLength + activeBow?.drawWeight + activeBow?.type; effect mirrors them into local state.
  • Issue B · Build Review and Spine Review disagreed. Two completely different algorithms — SpineCheck used recommendSpine (Easton effective-DW), Spine Calculator used recommendStaticSpine (dynamic-spine back-solve). Even with the same inputs, verdicts could differ. Fix: extracted recommendSpineFromInputs from spine.ts; both surfaces now derive the verdict from the same canonical algorithm.
  • Issue C · Algorithm too aggressive at boundaries. Old code flipped "OK" to "TOO WEAK" the moment effective DW crossed a chart-row boundary with no tolerance. Added a ±25 spine tolerance buffer so selected ∈ [recMin − 25, recMax + 25] reads as borderline-OK instead of failed.
  • Issue D · Draw length labeled as AMO. All draw-length inputs across BowSetupCard / RecommenderView / SpineCalculatorView are now labeled "Draw length AMO" with an info-icon popover explaining the AMO standard.
v135June 2, 2026

Share engine — every build, session, and overlay becomes a 1200×630 OG card

  • New Share button on three surfaces. Wizard Launch view (build snapshot), Range Log session row (session snapshot), Compare overlay (multi-source snapshot). Mobile-first slide-up modal with PNG preview + three actions: Open share sheet, Copy link, Download PNG.
  • Pure-SVG card composer. 1200×630 ember-on-near-black canvas matching the v126 OG language.
  • Privacy-first defaults. NO PII shipped by default — no name, no GPS, no per-shot photo. Two opt-in toggles for label + target-photo.
  • Public sharing URL at /pages/share?k=<b|s|c>&p=<base64>. The link IS the data — no server upload, no auth.
  • 9 new Vitest tests. 176/176 passing.
v134June 2, 2026

Photo target — CV-assisted arrow-hole auto-detection (Stage 2)

  • New "Auto-detect" button in the Photo Target overlay. OpenCV.js loads from CDN on first use (~2 MB gzipped, one-time per device).
  • Detection pipeline: photo → grayscale → Gaussian blur → HoughCircles + adaptive threshold + contour fallback → dedup in calibrated INCH space → ±18″ plausibility filter.
  • 9 new Vitest tests. 159/159 passing.
v133June 2, 2026

Photo-Anchored Logging — calibrate a target photo, tap each arrow hole, log every shot in inches

  • 2-point calibration + tap-to-place plot + schema-stable canonical inches storage.
  • 11 new Vitest tests. 150/150 passing.
v132.1June 2, 2026

BUG · fixedWelcome modal re-prompted every visit — ack now survives Safari ITP + code-version bumps

  • Ack key text-pegged sparrowForgeWelcomeAck.disclaimer-v1. Legacy v129 honored.
  • postMessage host bridge for first-party storefront localStorage round-trip.
v132June 2, 2026

Range Log Advanced — Sets, Precision Plot, Comparison Overlay

  • Quick ↔ Advanced capture toggle. Sets within a session (v3 schema). Compare Overlay sub-tab.
  • 10 new Vitest tests. 132/132 passing.
v131June 2, 2026

Compare Calculators — head-to-head benchmark page (/pages/compare)

v130 · v130.1 · v130.2June 2, 2026

Range Log "dialed in" — shot-by-shot plotting + session tracking

v129June 2, 2026

Mobile-first pass + Safety & Legal disclaimers

v128June 2, 2026

Pin Layout & Caliper precision tool (Sight Tape)

v127June 2, 2026

New emblem · C-v4 wired across the site

v126June 2, 2026

OG share image v2 — CFD with banded isobars

v125June 2, 2026

SVGDiagram full aerospace treatment

v124June 2, 2026

Field Notes — The 70-Yard Bareshaft Standard

v123June 2, 2026

Field Notes — Why You Miss Left From a Tree Stand

v122June 2, 2026

String stopper InfoCard — Pre-flight step

v121June 2, 2026

WindDriftCard CFD overlay

v120June 2, 2026

Methods page repositioning + FOC popover lean rewrite

v119June 2, 2026

Sight axis calibration + French tune

v117June 2, 2026

Methods page published

v115June 2, 2026

Reframe of v111FOC framework rebuilt as Sparrow Hybrid Model

v114June 2, 2026

Arrow Prep step + 70-yard bareshaft progression

v113June 2, 2026

Nock Fit sub-card + classification banners

v111June 2, 2026

Partially reversed in v115Trajectory plot + initial Ashby EFOC framework

v110June 2, 2026

Wizard Save wiring + step reorder + Express-tape guard + DA tests

v109June 2, 2026

HIGH-PRIORITY BUG · fixedDensity-altitude unit-conversion fix + Wizard hotfixes

The Forge predates this log — pre-v109 shipped during a more compressed sprint. Going forward, every release is documented here within hours of going live.

If you find a bug or a wrong claim, write to sal@sparrowexpeditions.com. I want to keep it honest.

— Sal Misseri, Sparrow Expeditions · Chicago · June 2026