Recommending the Best Time to Fish: Weather, Tide, and Solunar Theory
The Plan Trip tab combines a real weather/tide forecast with solunar fish-activity theory to recommend a time window — and the fishing-method filter bug that shipped before the useful version did.
Commit 3d75858 — "trip recommendations - real weather/tide forecast, solunar fish activity, and best practice suggestions" — is the moment the "Plan Trip" tab stopped being a form that just echoed back the forecast and started being something that actually recommended a when. The follow-up commit, 88c427c, fixes a fishing-method filter bug and a moon-phase display bug from the same feature two days later, which is the normal shape of shipping something this speculative on a solo project: get the theory working, then fix what breaks once real dates get typed in.
Solunar theory sets the shortlist of candidate times; weather and tide veto the bad ones.
What solunar theory actually claims
Solunar theory holds that fish (and game generally) feed more actively during specific windows tied to the moon's position relative to the observer — roughly two "major" periods (moon overhead/underfoot) and two shorter "minor" periods (moonrise/moonset) each day. It's not settled science, but it's widely believed and referenced among recreational anglers, and Mauritius fishing forums use the same vocabulary. Building it into the recommendation engine wasn't a scientific claim — it's a heuristic that matches the mental model users already bring.
function calculateSolunarPeriods(date, latitude, longitude) {
const moonTimes = await getMoonriseMoonset(date);
const majorPeriods = [
{ start: moonTransitTime, duration: 90 },
{ start: moonUnderfootTime, duration: 90 },
];
const minorPeriods = [
{ start: moonTimes.rise, duration: 45 },
{ start: moonTimes.set, duration: 45 },
];
return { majorPeriods, minorPeriods };
}Note the await — this became an async function specifically because moonrise/moonset now comes from the Meteo Mauritius scraper rather than a synchronous approximation, which is the change documented in METEO_MAURITIUS_INTEGRATION.md's "before/after" section. Solunar timing is only as good as the underlying moonrise/moonset data feeding it.
Weather and tide as vetoes, not inputs to a score
The interesting design decision is that solunar periods aren't blended into a single weighted score with weather and tide — they're a shortlist that weather and tide then filter. A "major" solunar window at 3 AM during a storm warning isn't a slightly-lower-scoring recommendation; it's excluded outright. The scoring function looks like:
function scoreTimeSlot(slot, forecast, solunarPeriods) {
if (forecast.windSpeed > 30 || forecast.waveHeight > 2.5) return null; // vetoed
let score = 50;
if (isWithinSolunarPeriod(slot, solunarPeriods.majorPeriods)) score += 30;
else if (isWithinSolunarPeriod(slot, solunarPeriods.minorPeriods)) score += 15;
if (forecast.tideState === 'rising' || forecast.tideState === 'falling') score += 10;
return score;
}Returning null for vetoed slots rather than a low score keeps the "best practice suggestions" text honest — the UI can say "conditions are dangerous, no recommended window today" instead of quietly recommending the least-bad option in a 40-knot wind.
The bug that shipped two days later
88c427c fixed a fishing-method filter — recommendations for a "casting" trip were surfacing bait suggestions meant for "couler" — plus a moon-phase display bug where the emoji shown didn't match the phase name in the text next to it. Neither bug affected the scoring logic; both affected trust in the feature. A recommendation engine that gets the display of moon phase wrong undermines confidence in the parts users can't independently verify, like the score itself. Fixing display bugs fast, even trivial ones, is part of making a heuristic-based feature feel credible.
Community predictions as validation
The "Predictions" tab, which compares today's conditions against the conditions of a user's most successful past trips, is effectively a second, independent check on the same solunar-plus-weather model — built from real logged outcomes instead of theory. When the two roughly agree, that's the closest thing this app has to evidence the heuristic isn't just noise.
Series: Fishing Tracker Pro. Next: what a year of CodeQL alerts looks like on a project built and reviewed by exactly one person.