feat(predicate): predicate definition + partial hierarchy explainer (#1358) - #1359
feat(predicate): predicate definition + partial hierarchy explainer (#1358)#1359kevinschaper wants to merge 7 commits into
Conversation
…1358) Add an info affordance to AppPredicateBadge that opens a modal explaining the predicate: its biolink definition plus a partial is_a hierarchy (and domain/range/inverse when present). Reuses the existing useBiolinkModel; adds a getPredicateAncestors helper that walks is_a. The biolink model is loaded lazily on first open (not per badge), and it degrades gracefully when a predicate isn't in the model. Claude-Session: https://claude.ai/code/session_018c9UddtKsKtm35YNrxHWuL
✅ Deploy Preview for monarch-app ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1359 +/- ##
==========================================
+ Coverage 83.17% 83.34% +0.16%
==========================================
Files 126 126
Lines 6617 6648 +31
==========================================
+ Hits 5504 5541 +37
+ Misses 1113 1107 -6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Review: predicate definition + partial hierarchy explainer (#1358)Nice, self-contained first cut — reuses the existing OverviewAdds Correctness
Performance
Test coverage
SecurityNo concerns — fetch target and YAML parsing are pre-existing, and the new template interpolates via Style/conventionsMatches the codebase well: Overall solid groundwork — the main things I'd want resolved before merge are the density/placement question (already flagged by you) and the per-instance model-loading cost, since they're related: fewer/shared modal instances would mitigate both. |
…o details modal - Explainer modal now clearly attributes the definition to the Biolink Model and links to the predicate's biolink-model docs page. - Drop domain/range from the explainer (keep the is_a hierarchy and inverse). - Also surface the predicate definition inline in the association details modal (SectionAssociationDetails), with the same Biolink Model attribution/link. Claude-Session: https://claude.ai/code/session_018c9UddtKsKtm35YNrxHWuL
Review: predicate definition + hierarchy explainerNice feature — reusing Potential bug: fetch errors are indistinguishable from "not found"
<AppStatus v-else code="warning">
No definition found for this relationship in the biolink model.
</AppStatus>— which reads the same as a predicate that's legitimately absent from the model. Performance: no de-duplication across concurrent loads
Duplicated formatting/URL logicThe "strip
...and the Biolink docs URL builder ( Minor
Test coverageOnly the pure Accessibility / density (already flagged in the PR description)Agreed this needs to be resolved before merging: putting an Overall this is a well-scoped first cut with a sensible lazy-loading design — the error-swallowing and duplicated-logic points are the two I'd prioritize fixing before merge; the rest are smaller nits. |
…heading - Address the review's performance concern: make useBiolinkModel a module-level singleton so the (large) biolink model is fetched + parsed at most once per session and shared across every AppPredicateInfo instance, instead of re-parsing per badge. Concurrent loads share one in-flight promise. - AppPredicateBadge: keep the info icon attached to the predicate label in constrained columns — scope overflow-wrap:anywhere to the label and keep the icon nowrap/adjacent, so it no longer orphans onto its own line. - Rename the explainer's "Hierarchy" heading to "Biolink Model Predicate Hierarchy". Claude-Session: https://claude.ai/code/session_018c9UddtKsKtm35YNrxHWuL
Review: Predicate definition + hierarchy explainerOverviewNice, well-scoped first cut. Bugs / correctness
Code quality
Performance
SecurityNo concerns. No new Test coverage
Notes on open questions in the PR descriptionThe placement/density and tooltip-vs-modal questions you raised seem like the right things to resolve before taking this out of draft — agree that an icon on every association-table row could get visually noisy; a hover tooltip for the definition with the modal reserved for the fuller hierarchy seems like a reasonable middle ground given the existing |
…tree Extend the explainer's hierarchy from just the is_a ancestors to a small graph-style tree: parent predicates (general -> specific), the current predicate (highlighted), and its direct child predicates, each linking to its biolink docs page. Adds getPredicateChildren() (slots whose is_a is this predicate). Children are capped with a "+ N more" toggle. Tested. Claude-Session: https://claude.ai/code/session_018c9UddtKsKtm35YNrxHWuL
Review: Predicate definition + hierarchy explainer (#1358)Nice feature — the modal design (definition + partial Bugs / correctness
Design / consistency questions
Test coverage
Minor
Overall a solid first cut with clean architecture; the race condition and swallowed-error cases above are what I'd actually fix before taking this out of draft — the rest are worth a look but not blocking. |
…ldren Render the predicate hierarchy as an indented tree (parents / current / children with spine + tick connectors), mirroring the node-page hierarchy preview, instead of a fan-out graph. Also fix getPredicateChildren to match child predicates attached via `mixins` (not just `is_a`), so e.g. biolink:treats now surfaces "ameliorates condition" and "preventative for condition". Claude-Session: https://claude.ai/code/session_018c9UddtKsKtm35YNrxHWuL
Review: predicate definition + partial hierarchy explainerNice feature — reusing the existing Potential bugs
Code quality / conventions
Test coverage
Security
Other notes
Overall this is a well-structured first cut — the singleton composable refactor is a nice unrelated improvement, and the hierarchy tree UI is a thoughtful way to show partial |
- AppPredicateInfo now shows a distinct "couldn't load the Biolink Model" error state instead of misreporting a network/parse failure as "no definition found". - Store the model in shallowRef (no deep-proxy overhead on the large tree that getPredicateChildren scans on every open). - Guard the getPredicateInfo alias loop against null slot values, and add a visited-set cycle guard to getPredicateAncestors. - Tests: alias resolution + is_a cycle termination. Claude-Session: https://claude.ai/code/session_018c9UddtKsKtm35YNrxHWuL
Review: predicate definition + partial hierarchy explainer (#1358)Nice feature, and the lazy-load-on-first-open design plus the module-level singleton cache in 🐛 Likely bug: highlighted predicate breaks the explainer on association tables
const predicate = computed(
() =>
props?.association?.highlighting?.predicate?.[0] ??
props.association.predicate,
);
const predicateString = computed(() => {
const value = predicate.value;
return (Array.isArray(value) ? value[0] : value) ?? "";
});
Concretely, on
Suggest deriving Minor: shared composable state now has cross-component side effects
Turning Test coverageThe composable functions ( Style / consistency
Nit
Nothing here blocks the underlying approach — the lazy-load architecture and hierarchy-walking logic are solid. The highlighting bug is the one I'd fix before this leaves draft, since it affects the primary use case (association table rows) called out in the PR description. |
…nship" The association-details definition row and the (i) explainer modal now read "Predicate definition: <predicate>" instead of "Relationship", making clear it is the predicate's definition and naming the predicate. Claude-Session: https://claude.ai/code/session_018c9UddtKsKtm35YNrxHWuL
Review: predicate definition + partial hierarchy explainer (#1358)Nice feature and the module-level singleton refactor of 🐛 Bug: stale predicate info when a badge instance is reused for a different predicate
async function onOpen() {
show.value = true;
if (info.value) return; // <-- not keyed on props.predicate
await loadBiolinkModel();
info.value = getPredicateInfo(props.predicate);
ancestors.value = getPredicateAncestors(props.predicate);
children.value = getPredicateChildren(props.predicate);
}
Fix: invalidate the cache when
|
Draft — first cut of #1358 (predicate explainers).
What
Predicates now carry an unobtrusive info icon (
AppPredicateBadge) that opens a modal explaining the relationship:is_ahierarchy (most-specific → general), andHow
useBiolinkModel(loadsbiolink-model.yaml, cached 24h in localStorage) and itsgetPredicateInfo.getPredicateAncestors(predicate, depth)that walksis_a.AppPredicateInfo.vue— the icon + modal; the biolink model is loaded lazily on first open (not per badge), so there's no cost until a user asks. Degrades gracefully when a predicate isn't in the model.Tests
getPredicateAncestorsunit-tested (chain walk, depth limit, unknown predicate) by injecting a fake model via the localStorage cache.vue-tsc+ eslint clean.Open UX questions (for review)
getPredicateInfoalready returns.Motivating case: the drug-indications section's
treatsvstreats_or_applied_or_studied_to_treat, and LOINC'scorrelated_withvsrelated_to.https://claude.ai/code/session_018c9UddtKsKtm35YNrxHWuL