Product backlog
Ideas we have agreed to explore but have not shipped yet. When work starts it moves to our dev changelog; when it ships it appears in release notes.
Deploy hygiene (operator)
After the first prod promote that ships [releases/2026-07-17.md](./releases/2026-07-17.md), delete all of these scripts and their calls in `prisma-post-db-push.mjs`:
- `deploy-seed-category-fallback.ts`
- `deploy-backfill-top-picks-scores.ts`
- `deploy-tag-date-night.ts`
- `deploy-platform-cron-defaults.ts`
- `deploy-purge-we-ha-ai-phantoms.ts`
- `deploy-scrape-target-bootstrap.ts`
- `deploy-normalize-scrape-event-slug-queue.ts`
- `deploy-oakdale-official-listing-url.ts`
- `deploy-peoplesbank-arena-listing-url.ts`
- `deploy-blox-things-to-do-listing-url.ts`
- `deploy-ajax-shell-playwright-locks.ts`
- `deploy-clear-playwright-first-tribe-direct.ts` (optional later: drop `playwrightFirstEvidenceUnlockedAt`)
- `deploy-merge-venue-name-phantoms.ts`
- `deploy-publisher-venue-heal.ts` (optional later: drop `publisherVenueHealDeployedAt`)
- `deploy-normalize-merged-away-events.ts` (`EventSlugAlias` keeps redirects after cleanup)
- `deploy-crawl-qc-run-plans.ts` (optional later: drop `crawlQcRunPlansDeployedAt`)
- `deploy-venue-address-identity.ts` (optional later: drop `venueAddressIdentityBackfillAt`)
Do not leave “REMOVE after promote” hooks wired forever.
---
Account & engagement
- User accounts — Optional login to persist saved events, home town / radius, and hearts across devices. On first sign-in, merge the anonymous `the860_harted` cookie into the account. Scope auth provider (magic link vs OAuth) when there is enough traction to justify it. *(Hearts ship cookie-only today.)*
---
Site / public
- Venue detail page — Places-rich redesign — Public `/venues/[slug]` (and listing cards) currently show name/town/address/logo/description/website only. After Places/Foursquare identity + first-pass enrich, surface richer on-file facts: Google rating, hours, parking/accessibility/dining highlights from snapshots/`venueTips`, clearer logo vs hero art. Keep voice/layout consistent; do not dump raw API JSON.
- Venue image gallery (curated hero + logo) ↳ partial — unified missing-photo UX + Venue Hero / Venue Logo naming shipped. Remaining: optional gallery and admin upload; AI-assisted Venue Hero candidate picking is future work (current ranking is deterministic).
- Homepage back-navigation performance ↳ partial — SSR near-you reuse, static root layout, home `loading.tsx`, remount/prefetch trim shipped. Remaining (lower priority): EventCard `prefetch={true}` site-wide; `/api/events/filters` on home mount; localStorage hydration second fetch when cookie ≠ storage.
- Homepage / detail client bundle ↳ partial — `EventDescription` server-rendered and browse/home rows slimmed. Remaining: confirm event-detail client components don’t pull the scrape graph under webpack; keep scrape/server modules out of card UI; optional lazy `EventHartControl` on listing cards.
- `/api/events` memory-pool row ceiling ↳ partial — Top Picks (including town/category/date/Near bbox/map) is DB `ORDER BY topPicksScore, startsAt`. Remaining: same discipline for `recommended` / `distance` memory pools; Top Picks still uses ≤`TOP_PICKS_CANDIDATE_CEILING` only for `q` / today / time-of-day memory passes.
- Better “cool” scoring + discover more top events ↳ partial — Persisted coolness + admin pin/edit shipped. Remaining: scoring formula tweaks / surfacing more hidden gems.
- SEO landing pages (phase 2) — More towns (Enfield, Torrington, Meriden, Simsbury, Wethersfield, Mystic/Essex) and seasonal copy; Search Console after index. *(Trivia Near Me v1 + filter UI shipped.)*
- Release note visitor-copy lint — Checklist in team-norms so “For visitors” passes [vision-and-brand.md](./vision-and-brand.md) before promote.
- Security doc — Admin secret model and `/api/event-image` SSRF notes (held per owner request).
---
Scrape / data
- Periodic missing-image review ↳ partial — Shipped: first missing photo → BugReport; later → soft flag; QC product defaults On via deploy hook on first prod promote. Remaining: broader offline gallery / Missing Image Heros pool (Admin / imagery).
- Adaptive ingestion — run-plan learning ↳ partial — structured feeds, locks, learned caps, Needs run plan guidance shipped. Remaining: optional richer learning from crawl patterns, per-source “learning on event recrawl,” and a persisted admin checkbox (vs derived badge).
- Facebook event sources ↳ partial — Shipped: tab outcome badges + auto-disable after repeated blocked/no-tab crawls; tiered cadence floors FB listings at weekly. Remaining: many FB listing URLs still return 0 extracted — structural listing strategy (or official API) needed before broad FB cadence; login/ToS limit browser depth.
- Per-publisher crawl session — `ScrapeTarget.extraPrompt` + run-plan locks (not global env) for market cookies (AXS Hartford zip today), logged-in Facebook/Eventbrite as the860life, etc. AXS default zip lives in `SCRAPE_PIPELINE.axsDefaultZip` until per-source prose owns it.
- Admin: unmerge / split Locations ↳ partial — Unmerge restores child + `preventAutoMerge`, relinks label-matching upcoming events, queues bounded source recrawls. Remaining: richer confirmation UI with event counts; optional `preventAutoMerge` on the winner when operators want both sides locked.
- Admin: location merge audit trail — Persist a lightweight log when venue sync auto-merges two `Location` rows: timestamp, merge reason, before/after ids, optional `eventId` samples. Surface read-only on the venue detail row.
- Generalized co-production venue rules — Hartford.com co-presented shows beyond `co-production-venue.ts` unite/co-production copy parser.
- OpenAI pipeline ordering UX — Long recrawls can show publish progress then “spacing OpenAI calls”; clarify or reorder so Jobs progress matches operator mental model. *(Bulk 860 Take throttle for >12 events ships; Gemini failover + durable TPM notes ship.)*
- Alfred ingest API (optional) — If the browser worker splits off the HP machine, a small authenticated ingest endpoint for rendered HTML instead of shared DB queue drain only. ADR: [0007](./decisions/0007-playwright-oracle-vm.md); ops: [alfred-setup.md](./alfred-setup.md).
- Accessibility-tree crawl extraction — After a listing loads in Playwright on Alfred, try accessibility snapshot alongside stripped HTML and JSON-LD when rendered HTML is unusable or extract count is zero.
- Recrawl on empty-but-valid feeds ↳ partial — Trusted-empty withdraw for Facebook + trusted structured paths ships; generic HTML/AI zero-extract still skips reconcile (see [known-issues.md](./known-issues.md)). Remaining: clearer operator signal when a feed is valid but returns zero events.
- RSS / feed auto-registration — Detect new CivicLift, ICS, and Tribe feeds during crawl and surface “add as source” in admin. CivicLift/CitySpark ingestion lock for existing sources ships in `structured-feed-lock.ts`.
- E2E smoke (crawl) — One Preview crawl test against fixture HTML in CI.
- Mobile event-page screenshot — Playwright phone-width capture when listing + `og:image` fail.
- Concise crawled event titles (SEO) ↳ partial — sports strip + occurrence/date suffixes + redundant `at {venue}` strip + prefer shorter compatible titles on recrawl + concert title guidance shipped. Remaining: broader marketing-tagline shortening at extract for all publishers.
- Refresh compact occurrence titles — Re-crawl recurring series for `Sun 5/8`-style titles (Tribe occurrence URLs help HJS but not all publishers).
- Trivia image from social — Facebook/Instagram art before generated trivia cards.
- Crawl cost dashboard — Admin view rolling up per-`ScrapeTarget` cost signals from `ScrapeRun` (pages fetched, detail skips, Alfred wall time, AI token usage). Sort by spend last 7d / 30d.
- Missing-category crawl audit — Post-crawl or scheduled script listing drafts/events dropped for missing/invalid category after guidance + AI pass. Group by `ScrapeTarget` + extraction path.
- Legacy rows missing externalId — audit prod before delete ↳ partial — Events admin can filter/select/delete missing-`externalId` rows. Remaining: read-only prod count/classify script for operator sign-off before bulk delete.
- Listing-only crawl must always yield stable externalId ↳ partial — extract rejects missing id; finalize/publish block blank `externalId`. Remaining: ensure every listing-only path synthesizes a stable structural id when publisher omits one.
---
Crawl cadence & self-healing QC
Shipped on `dev` (promote prep 2026-07-17): tiered cadence (12h imminence floor + pin + Platform caps); no auto SCRAPE_RESET; Extra scrape prompt stays operator-edited; Jobs `EVENT_QC`; missing photo first → BugReport, later soft flag; live schema/ICS confirmation for time/title findings; first prod deploy flips QC + auto suggested recrawls On.
↳ partial remaining: - Stale worker cancel thresholds — further tune Alfred hung-row cancel/re-enqueue if overlap still trips “no progress” in practice. - Periodic missing-image review gallery/backfill UX beyond `imageMissingFlaggedAt` (ties Admin / imagery).
---
Ops
- Concise crawled event titles (SEO) — see Scrape / data.
---
Deployment / platform health
Baseline on `main`: [smoke-test.md](./smoke-test.md) (`verify:deployed:dev` / `:prod` / `verify:promote`), CI after prod→dev copy, [database-release-workflow.md](./database-release-workflow.md).
- Public deployment status page — e.g. `/status` from `/api/health` (no secrets in UI).
- Admin deployment status — e.g. `/admin/status` with schema parity, event counts, Actions links.
- `GET /api/deploy-health` — One JSON payload for monitors and status UI.
- External uptime monitoring — Document Better Stack / UptimeRobot polling www + dev health.
---
Admin / imagery
- Category on uploaded hero ↳ partial — category mapping ships on Category Missing Images (multi-select). Remaining only if HP Hero Images themselves need category tags for homepage rotation (not requested).
- Exclude from HP banner rotation — Per-image flag so a hero can serve missing-image fallbacks without entering the homepage banner carousel. *(Category Missing Images is now a separate pool — this flag may be obsolete.)*
---
Venue / admin guards
- Venue create guard ↳ partial — admin Add venue + crawl ensure share `resolveVenueLocation` (admin-guard / ensure). Remaining: surface the existing row “open instead” UX polish if operators still get confused; complements Admin: unmerge / split Locations.
