diff --git a/.gsd/milestones/M010/M010-ROADMAP.md b/.gsd/milestones/M010/M010-ROADMAP.md index 3ef6283..d49d64a 100644 --- a/.gsd/milestones/M010/M010-ROADMAP.md +++ b/.gsd/milestones/M010/M010-ROADMAP.md @@ -7,6 +7,6 @@ Chrysopedia should feel like exploring a music production library, not querying | ID | Slice | Risk | Depends | Done | After this | |----|-------|------|---------|------|------------| | S01 | Dedicated Sub-Topic Pages | high | — | ✅ | Clicking 'Compression' on Topics page loads /topics/mixing/compression with techniques grouped by creator, description, breadcrumbs | -| S02 | Related Techniques Cross-Linking | medium | — | ⬜ | Bottom of every technique page shows 3-4 related techniques from same creator or same sub-topic | +| S02 | Related Techniques Cross-Linking | medium | — | ✅ | Bottom of every technique page shows 3-4 related techniques from same creator or same sub-topic | | S03 | Topic Color Coding & Visual Polish | medium | S01 | ⬜ | Each of 7 topic categories has a distinct accent color in badges, cards, and sub-topic pages. Page transitions are smooth. Creator avatars are visually unique. | | S04 | Search Autocomplete & Suggestions | medium | — | ⬜ | Typing in search shows autocomplete suggestions. Empty search box shows popular search terms. | diff --git a/.gsd/milestones/M010/slices/S02/S02-SUMMARY.md b/.gsd/milestones/M010/slices/S02/S02-SUMMARY.md new file mode 100644 index 0000000..bec25f6 --- /dev/null +++ b/.gsd/milestones/M010/slices/S02/S02-SUMMARY.md @@ -0,0 +1,92 @@ +--- +id: S02 +parent: M010 +milestone: M010 +provides: + - Dynamic related techniques endpoint (up to 4 scored results per technique page) + - RelatedLinkItem schema with creator_name, topic_category, reason fields + - Responsive related-card CSS grid component +requires: + [] +affects: + - S03 +key_files: + - backend/schemas.py + - backend/routers/techniques.py + - backend/tests/test_public_api.py + - frontend/src/api/public-client.ts + - frontend/src/pages/TechniquePage.tsx + - frontend/src/App.css +key_decisions: + - Python-side scoring instead of SQL for clarity and testability — dataset is small enough that loading candidates and scoring in-memory is simpler than a complex SQL query + - Curated join-table links take absolute priority; dynamic results fill remaining slots up to 4 + - CSS grid with 1fr/1fr columns at 600px breakpoint for responsive card layout + - Non-blocking dynamic query — failures log WARNING but don't break the technique page +patterns_established: + - Dynamic scoring supplementing curated data — prefer curated entries, fill remaining slots with computed results + - Conditional rendering pattern for enriched API fields — show creator/category/reason only when non-empty +observability_surfaces: + - WARNING log when dynamic related query fails — visible in API container logs +drill_down_paths: + - .gsd/milestones/M010/slices/S02/tasks/T01-SUMMARY.md + - .gsd/milestones/M010/slices/S02/tasks/T02-SUMMARY.md +duration: "" +verification_result: passed +completed_at: 2026-03-31T06:19:54.667Z +blocker_discovered: false +--- + +# S02: Related Techniques Cross-Linking + +**Every technique page now shows up to 4 related techniques scored by creator overlap, topic category match, and shared tags — rendered as a responsive card grid with creator name, category badge, and reason text.** + +## What Happened + +This slice replaced the empty join-table-based related links with a dynamic scoring system and updated the frontend from a plain list to a card grid. + +**T01 (Backend):** Added `_find_dynamic_related()` helper in `routers/techniques.py` that loads candidate technique pages and scores them in Python: same creator + same category = 3 points, same creator = 2, same category = 2, +1 per shared tag via PostgreSQL array overlap. Results are capped at 4, ordered by score descending. Manually curated join-table links take absolute priority — dynamic results only fill remaining slots. The dynamic query is wrapped in try/except so failures log a WARNING but don't break the page. Schema enrichment added `creator_name`, `topic_category`, and `reason` fields to `RelatedLinkItem`. Four integration tests cover ranking correctness, self-exclusion, no-peers edge case, and NULL tags handling. + +**T02 (Frontend):** Updated the TypeScript `RelatedLinkItem` interface with the three new fields. Replaced the `