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.
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.
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.
CRITICAL BUG · fixedCross-origin parent.location read crashed the entire bundle
-
The bug.
useIdentity.tsxreadwindow.parent?.location?.searchto detect the?signedin=1redirect param. The optional chaining caughtwindow.parentbeing undefined but NOT the cross-originSecurityErrorthrown when reading.locationon 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: sandboxexcludingallow-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). AuditedidentityBridge.ts+useCloudSync.tsfor 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.
Share button Messenger-hijack fix + cut-length guardrail
-
Share fix. iframe
allowattribute now includesweb-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. Withweb-shareadded, the native iOS share sheet appears as expected. -
Download fix.
downloadSvgAsPngnow triesnavigator.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.
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'suseEffectre-synced shaft/point/insert/nock fields whenactiveSetup.idoractiveArrowIdxchanged — 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 includeactiveBow?.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 usedrecommendStaticSpine(dynamic-spine back-solve). Even with the same inputs, verdicts could differ. Fix: extractedrecommendSpineFromInputsfromspine.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
±25spine 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.
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.
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.
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.
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.
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.
Compare Calculators — head-to-head benchmark page (/pages/compare)
Range Log "dialed in" — shot-by-shot plotting + session tracking
Mobile-first pass + Safety & Legal disclaimers
Pin Layout & Caliper precision tool (Sight Tape)
New emblem · C-v4 wired across the site
OG share image v2 — CFD with banded isobars
SVGDiagram full aerospace treatment
Field Notes — The 70-Yard Bareshaft Standard
Field Notes — Why You Miss Left From a Tree Stand
String stopper InfoCard — Pre-flight step
WindDriftCard CFD overlay
Methods page repositioning + FOC popover lean rewrite
Sight axis calibration + French tune
Methods page published
Reframe of v111FOC framework rebuilt as Sparrow Hybrid Model
Arrow Prep step + 70-yard bareshaft progression
Nock Fit sub-card + classification banners
Partially reversed in v115Trajectory plot + initial Ashby EFOC framework
Wizard Save wiring + step reorder + Express-tape guard + DA tests
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