sweep/2026-09-30: 9H + 46M bugs fixed, 14 refactors (notes parser, cities, dataStore, map, feedback) - #35
Open
Chartres wants to merge 81 commits into
Open
sweep/2026-09-30: 9H + 46M bugs fixed, 14 refactors (notes parser, cities, dataStore, map, feedback)#35Chartres wants to merge 81 commits into
Chartres wants to merge 81 commits into
Conversation
…nday ?den=nedele on a Sunday now decodes to key=7 (next Sunday) instead of 0 (today). off=0 matched because today IS that weekday, but dayToParam(0) always produces 'dnes', so a weekday slug can only mean next occurrence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Raw fetch('/data/services/…') bypassed the OTA filesystem cache on native.
Switch to loadData() so native OTA-refreshed registry and detail data
stay in sync, matching the pattern already used for photos and confession.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers the previously untested case: ?den=nedele on a Sunday must decode to key=7 (next Sunday), not 0. Also verifies the non-Sunday case (Friday → off=2). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
new Date(`YYYY-MMT HH:MM`) is local-timezone; users far east of Prague got a Prague weekday one day off. Use T12:00:00Z so the UTC timestamp always falls on the correct Prague calendar date regardless of locale. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
clusterChurches used visible (viewport subset) when witness tags were active, so cluster boundaries shifted on pan. Use churches (full index) filtered by witness tags, matching the pan-invariant pattern used for the unfiltered case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…view Removes the TODO foot-gun: production builds (no VITE_WITNESS_PREVIEW) now require 3 independent witnesses before a chip is publicly visible. Prototype/TestFlight builds (VITE_WITNESS_PREVIEW=1) keep min=1. A separate VITE_CORROBORATION_ONE=1 is set in test env to preserve the local-store test behaviour without enabling WITNESS_ENABLED in App tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
No production code outside cities.ts imported them; only the test file did. Removing the exports and their direct test cases — the behaviour is covered by the aggregateCities and searchPlaces integration tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pies Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ced by the season banner Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…alse 401 is in KEEP_STATUS so rounds that return 401 are not hard-fails. The old mock consumed two m-increments per round (HEAD+GET fallback), making all three rounds land on 456; the assertion passed for the wrong reason. Group increments by round (Math.ceil(m/2)) so rounds 1 and 3 see 401 (not a hard fail) and round 2 sees 456 — correctly yielding drop:false. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…owns merging Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…o step back Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…r white-screen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t empty results Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…or midnight Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…le render Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… App re-render Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ed version Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-cache reads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…upabase query Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…(n) at country zoom Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ution) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n --cell/--limit runs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…arsing Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… and unreachable return Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
shardCache survived OTA refreshes (reloadKey bumps) so the map matched new church data against stale shard content. Added reloadKey prop; a useEffect clears the cache whenever it changes. App now passes reloadKey to MapView. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
?den=<weekday> offsets were computed from new Date() but clockTick was not a dependency, so midnight didn't retrigger the memo. Moved clockTick useState above the day memo so it can be listed in the dependency array. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DayPicker computed day labels/feasts once at mount (empty deps). After midnight the options were stale. Threaded clockTick through OrdoControls → DayPicker so the memo recomputes when the clock ticks. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contact URLs were written raw, leaving scheme-typo'd values (http:\\, http:/) in shards. normUrl already fixes these for institution.www; now applied to contacts too. Falls back to raw value when normUrl returns empty (plain www.). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Slug suffixes (-2, -3) were assigned in count order — a data refresh reordering the counts repointed existing /mesto/<slug>/ URLs. Now same-slug collisions are sorted by name (stable), so the suffix assignment is data-refresh-invariant. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ce re-export) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…heck The church aggregate folds every slot, so a witnessed slot always implies church.chips > 0 — the slot lookup was redundant (same check App uses). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
parseBound returned daysInMonth(2023, 2)=28 for the 'to' edge, so Feb 29 of a leap year failed the range check. Use 31 as a sentinel for all 'to' edges with konce — no real date exceeds day 31, so the next month's first day is never included. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
"ve školním roce, 1. sobota v měsíci" was matching every 1st Saturday (including July/August) because comma-separated inclusion segments were OR-ed. Czech schedule notes stack conditions — both must hold — so switch incls combiner from .some to .every. The existing excluded-in-months (kromě…) tests still pass; the mixed incl+excl case uses every on both. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CONDITIONAL missed "sudou/lichou neděli", "1. neděle" without "v měsíci", "v zimě", and "od Velikonoc do Dušiček". Add WEEKDAY_RE (which only fires on already-unparsed segments), zim, velikono, and dušič to the pattern so those notes set uncertain=true and render prominently instead of silently showing every week without a warning. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…he (M5) ready() sets cacheReady=false when the bundled snapshot is newer, but the old OTA timestamp stayed in localStorage.asOf, causing the footer to show a stale date and refreshData to skip the remote check (remoteKey <= stale asOf). Clear asOf at the same time ready() drops the cache. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Concurrent loadData calls during the shard+index write loop could read bundled churches.json alongside freshly-cached shards (or vice versa), yielding an inconsistent registry. A refreshLock promise is set before the first write and released (in finally) after all writes complete; loadData awaits it before checking ready(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Promise.all over 4 workers propagated any single fetchPolite rejection to the whole batch. Catch per-ID errors inside the worker loop so a 404/410 church is logged and skipped without aborting the remaining ~8000 fetches. Also switch outer Promise.all to allSettled for belt-and-suspenders. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
City pages linked up to 1800 /kostel/<id>/ URLs that had no prerendered file — crawlers got a 404. Collect the distinct churches referenced from the top-30 city pages, prerender each with withMeta (title, description, canonical), and add their URLs to sitemap.xml. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…formatter Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… (H1) Pre-process each sentence before the generic comma split: replace commas inside ordinal lists that precede a weekday with " a " so "1., 3. sobota v měsíci" becomes "1. a 3. sobota v měsíci" and reaches parseSegment as a single multi-nth predicate. Previously the split left "1." as a dangling segment, so the 1st occurrence was excluded when only the 3rd was queried. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
"v červenci, v srpnu" was ANDed (July AND August = never), now OR'd
(July OR August). "1. sobota, 3. sobota v měsíci" similarly OR'd.
Cross-kind pairs ("ve školním roce, 1. sobota") still AND'd.
Adds segKind() to classify segments as 'month', 'weekday', 'range',
or 'other'; groups inclusions by kind; ORs within a group, ANDs
across groups.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Math.min(zoom+2, 17) returned 17 when zoom was already 18, zooming the map out. Cap at 18 and guard against any zoom beyond 18 with Math.max. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The load-more button was gated on day === 'now', so when witness filter was active on a non-now ordo day (day !== 'now'), the list was silently truncated at listLimit with no way to page further. Extend the condition to also show when witnessTags are active. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Supabase silently truncates at 1000 rows; make the cap visible with an explicit .limit() call and a comment noting when pagination is needed. Update the supabase stub to chain .limit() so tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…counter (M6) A slow pre-submit loadAggregates call could resolve after a post-submit refresh and overwrite the cache with stale data. A module-level loadGen counter increments on each call; after the await, a gen mismatch means a newer call started — discard the result. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Supabase's functions.invoke() resolves (not rejects) even when the Edge Function returns an error. The previous code always called loadAggregates after the promise settled, so a failed submit triggered a pointless cache refresh that could overwrite the real post-submit data. Now only refresh when error is falsy. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the witness filter is active, cluster input was all churches for which churchHasTags returned true — which depends on which aggregates happen to be cached, giving pan-history-dependent cluster shapes. Fix: cluster only over churches whose aggregate has settled (loaded). Unloaded churches render at 40% opacity as "not yet assessed" rather than silently vanishing, making the distinction between "no witnesses" and "not checked yet" visible. Adds hasAggregate() to feedbackStore (cache.has check). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Service and ExtraService already satisfy OccurrenceSpec; drop the per-caller spec rebuilds in ics, ranking, native-actions and ChurchDetail. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
App.tsx carried a cs/en copy of the season_* strings already in i18n. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sweep 2026-09-30 — bohosluzby
5 cycles × (Red → Green → Refactor). Tests: 343 → 355 (12 new). All green.
Findings table
školní rokmatches before negation — July Masses hiddennot(nthWeekday)inverts all non-target weekdaysrowsnot refreshed after midnight/resume--cell/--limitoverwrites entire photos.jsondaymemo stale across midnight (missing clockTick dep)Refactors (14)
liturgical: reusepragueTodayfrom occurrencesapp: direct dayFromParam offset, no search loopsrc: unexport 6 module-private symbolsapp: reusefmtWeekdayShortfor day-picker labelsranking: usepragueIsoDatefor ordo day comparisonfeedback: hoist chip-order map to module constantmap: replace witnessTiers/hasWitness with church-tier checkapp: drop unused exports (litForChoice, parseRoute, DayChoice)domain: pass services directly as occurrence specsapp: read season labels from i18n season_* keysformat: dayLabel reads day_today/day_tomorrow from i18nics: build DTSTART from pragueIsoDatedataStore: web refresh reads version.json via loadDataformat: reuse pragueIsoDate instead of second date formatter🤖 Generated with Claude Code