Skip to content

feat: re-weight patron-saint quiz for multi-path discovery#210

Open
SimplyThomas wants to merge 4 commits into
mainfrom
feat/quiz-reweighting
Open

feat: re-weight patron-saint quiz for multi-path discovery#210
SimplyThomas wants to merge 4 commits into
mainfrom
feat/quiz-reweighting

Conversation

@SimplyThomas

Copy link
Copy Markdown
Owner

What & why

The patron-saint quiz (src/lib/quiz.ts) scores saints by weighted controlled-vocab facet overlap. Its weights were misaligned with how complete the underlying data is — it weighted the sparsest facet highest:

dimension old weight coverage (verified, 2,740 saints)
intercession 3 (highest) 18.6% (sparsest)
experience 2 56.0%
vocation 2 21.7%
family 2 24.1%
virtue 1 69.0%
tradition 1 92.4%
origin (Region) (no question) 97.0%

Because intercession was weighted highest but present for only ~18.6% of saints, the quiz systematically favored the ~500 intercession-tagged saints and gave the other ~81% little chance to surface — even when they matched strongly on story or calling. That contradicts the project's value model: per CLAUDE.md §1 the product is multi-path discovery (read/learn · share-a-name · relate to a story/life/vocation/background · need-matching), and §10 explicitly deprioritizes Intercessions as the narrowest path.

Coverage above was recomputed from the data, not taken on faith: python build.py finder-coverage for the four fields it reports, and a direct non-empty-cell count of data/saints.csv for Region of Origin / Family / Tradition (which it omits).

Changes (src/lib/quiz.ts)

  1. Re-weight toward the relatability paths. Story (experience) and Calling (vocation) lead at 3; Life (family), Region (origin), Virtue, and Intercession sit at 2; Tradition stays a light 1.
    • Intercession drops from the dominant 3 → 2: still a meaningful signal when present (above Tradition), but no longer dominant. A story+calling match now outranks an intercession-only match.
    • Rationale is coverage- and path-driven: Story/Calling are the two strongest "relate to a saint" paths in §1, so they lead; Intercession is the narrowest path (§10), so it is demoted, not removed.
  2. Add a Region/Background dimension (origin, ~97% covered, previously unused) — serves "find a saint from my background" (§1). Region was available in FinderSaint but the quiz had no question for it.
  3. Lead the flow with Story instead of the affliction path, mirroring the §1 reframe.
  4. Normalize per dimension. A matched dimension now contributes its weight once, regardless of how many values matched. Previously a single saint could run up the score by stacking many tags in one (sparse, high-weight) facet; now breadth across the relatability dimensions wins, and depth within one dimension is only a tie-break (by number of values matched, then name).
    • Note on "normalize by available signal": dividing a saint's score by the total weight of the facets it carries data for was considered and rejected — it would give a stub with only a Region tag a perfect 1.0 ratio, over-rewarding sparse saints. Capping each dimension's contribution achieves the goal ("sparse-but-high-weight facets don't dominate") without that pathology.

Tests

Adds src/lib/quiz.test.ts (Vitest — there was none before). It locks in the weighting and asserts:

  • a story/calling match outranks an intercession-only match (and even a single story match does);
  • per-dimension normalization: an intercession-only saint matching four intercession picks still loses to a single story match;
  • the new Region dimension scores; tie-break by overlap breadth; no-overlap saints are omitted.

Validation (all green)

  • make web-unit — 18 passed (10 new)
  • make web-lint — clean
  • make web-buildpython build.py + astro build both exit 0 (2,768 pages)

No saint data was changed — this is purely a scoring/weighting change.

Follow-up (out of scope, not bundled)

The finder's OPEN_BY_DEFAULT facet order in src/lib/filter.ts opens Intercessions first — the same misalignment with the multi-path goal. Worth a separate small PR.

Preview

https://feat-quiz-reweighting.orthodox-saints.pages.dev
(once the Cloudflare Pages check is green — previews render draft/flagged profiles)

🤖 Generated with Claude Code

The quiz scored saints by weighted facet overlap with Intercession as the
single highest weight (3) — yet Intercession is the sparsest facet (~18.6%
of 2,740 saints), so the ~500 tagged saints crowded out the other ~81% even
when those matched strongly on story or calling. That contradicts the
multi-path discovery goal (CLAUDE.md §1/§10), where Intercession is the
narrowest path and should not be over-indexed.

Changes (src/lib/quiz.ts):
- Re-weight toward the relatability paths: Story (experience) and Calling
  (vocation) lead at 3; Life (family), Region (origin), Virtue, and
  Intercession sit at 2; Tradition stays a light 1. Intercession drops from
  the dominant 3 to a meaningful mid weight.
- Add a Region/Background dimension (origin, ~97% covered, previously unused)
  — "find a saint from my background" (§1).
- Lead the question flow with Story rather than the affliction path.
- Normalize per dimension: a matched dimension contributes its weight ONCE,
  so a sparse-but-high-weight facet can no longer dominate by stacking many
  tags. Breadth across dimensions wins; depth within one is a tie-break.

Adds src/lib/quiz.test.ts (Vitest) locking in the weighting and asserting a
story/calling match outranks an intercession-only match. No saint data changed.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 23, 2026

Copy link
Copy Markdown

Deploying orthodox-saints with  Cloudflare Pages  Cloudflare Pages

Latest commit: 492b361
Status: ✅  Deploy successful!
Preview URL: https://5f0fd084.orthodox-saints.pages.dev
Branch Preview URL: https://feat-quiz-reweighting.orthodox-saints.pages.dev

View logs

The QUIZ config reweighting flows through the rendered flow automatically
(QuizForm.astro and quiz.client.ts iterate QUIZ), but the surrounding intro
and meta copy still foregrounded intercession ("lives and intercessions echo
your own"). Broaden it to the story/calling/background paths (§1) so the prose
matches the scoring.
@SimplyThomas

Copy link
Copy Markdown
Owner Author

Follow-up commit (5e9ebb9): aligned the quiz framing copy with the reweighting. The QUIZ config changes already flowed through the rendered flow (both QuizForm.astro and quiz.client.ts iterate QUIZ — so the quiz now shows 7 questions, Story-first, with the new "Your Background" question), but the intro lede, instructions, and meta description still said saints "whose lives and intercessions echo your own." Broadened that to the story/calling/background paths (§1) so the prose matches the scoring. Lint + build re-run green.

Previously the result panel led with the matched saint's full intercession
list ("Patron of …"), which could show patronage the seeker never selected and
hid the actual basis for the match. Replace it with a transparent, per-question
breakdown.

- quiz.ts: each match reason now carries its dimension ({ key, kicker, value })
  instead of a bare value string, so the UI can group and label matches.
- quiz.client.ts: the result panel renders a "Why you were matched" block
  listing, grouped by question (Your Story / Your Calling / Your Background / …),
  the exact values the seeker picked that this saint shares. Companion cards gain
  a compact shared-values line too.
- global.css: styling for the grouped match list and companion shared line.
- quiz.test.ts: lock the contract that reasons are tagged by question.

Verified in a built preview (Playwright): the block renders grouped rows and
companion shared lines. web-unit (19), web-lint, web-build all green.
@SimplyThomas

Copy link
Copy Markdown
Owner Author

Follow-up commit (22d98e1): result transparency. The result panel previously led with the matched saint's full intercession list ("Patron of …") — which could surface patronage the seeker never picked and hid the actual basis for the match. It now shows a "Why you were matched" block listing, grouped by question, the exact values the seeker selected that this saint shares (e.g. Your Story · Persecution · Torture / Your Calling · Writer / Your Background · Asia Minor). Companion cards gain a compact shared-values line too. Each match reason now carries its dimension ({ key, kicker, value }). Verified in a built preview with Playwright; web-unit (19) / web-lint / web-build all green.

…ded 6

The quiz e2e walkthrough hard-coded `for (i = 1; i < 6; ...)` to advance through
the question screens, assuming 6 questions. Adding the Region question made it 7,
so the loop stopped one screen short and never clicked "Reveal my patron" —
`.qz-patron` never appeared and the frontend gate failed. Derive the count from
the rendered `.qz-step` screens so it survives question changes, and assert the
new transparent "Why you were matched" block renders.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant