Repository navigation
Phase 2 — Joule learning-path generator #445
Description
Activity
PR #563 opens the implementation. Walkthrough of the design pivot for future readers:
The issue body's canonical SPARQL
?a kg:teaches/^kg:requires*/kg:teaches ?breturned 0 rows on the v3 graph when probed pre-implementation —kg:requiresis concept-level (not tutorial-level) and only 919 sparse edges exist (442 of 1357 concepts have any prereq). PR #563 ships a hybrid 3-arm pathBetween that uses what data IS dense:- PREREQ (rank 1) —
?a kg:teaches/(^kg:requires)+/kg:teaches ?b— preferred when prereq edges exist - CO_COMPLETED (rank 2) —
?a (kg:coCompletedWith)+ ?b— behavioral signal (~13k edges) - SHARED_CONCEPT (rank 3) —
?a kg:teaches ?c. ?b kg:teaches ?c.— always-on
Sorted by
pathTypeRank ASC,LIMIT 10. JS layer dedups by slug, promotes the LLM-namedtoSlugwhen present, filters user-covered candidates (toSlug never dropped), hydrates with title + estimated time, renders a numbered markdown list.Future-friendly: when Phase 2.5 prereq-graph enrichment lands, ARM 1 will produce results more reliably without code changes here — the dispatcher already favors PREREQ.
Acceptance criteria status
-
KnowledgeGraphService.pathBetween(fromSlug, toSlug)— wired via CDS action; gated byChatSettings.kgPathBetweenEnabled -
KnowledgeGraphService.conceptsForUser(userId)— wired via CDS action; helper atsrv/lib/kg/concepts-for-user.js - Joule chat with "I want to build a CAP service with Fiori UI" returns an ordered learning path — requires admin to flip
kgPathBetweenEnabled = truepost-deploy - Telemetry events fire (
kg.joule.path_requested,kg.joule.path_returned) - Hybrid + smoke tests pass — 14/14 hybrid, smoke skips cleanly when env vars unset
- Phase 2 rollout note at
docs/superpowers/done/— file as follow-up; gets filled in 48h post-flag-flip
Followups noted in the PR
- Phase 2.5 prereq enrichment — separate issue to file; densifies the prereq sub-graph so ARM 1 produces results more reliably
kg:completedByedge — Phase 4 architectural change for graph-side user identity; userIds currently stay out of the graph for privacy- Phase 3 Explore UI — Phase 3 — Explore page + concept landing pages #446 territory; pathBetween backend ready
Latent-bug pair caught during execution
Two pre-existing bugs from PR #555 surfaced when Phase 2 became the first production consumer of
.responsefrom the procedure layer:- TaskRecords schema mismatch — Task 4's helper used
TUTORIAL_IDbut CAP uses(taskLegacyId, taskType). Caught by Task 8's hybrid test. Fixed in885dc700. coerceRowdoesn't unwrap{ changes: [...] }—@cap-js/hanareturns DO-block results in a wrapper, and the existing helper only unwrapped flat arrays. Every priorkgQuery()/kgAdminRunSparql()caller silently got{ response: '' }. Caught when getConceptsForUser actually consumed.response. Fixed inb119f7df.
Both fixes ship in this PR.
- PREREQ (rank 1) —
- added a commit that references this issue
on Jun 23, 2026
Phase 2 — Joule learning-path generator
What this delivers
A new Joule chat tool that lets developers ask natural-language questions like:
cap-handlerstohana-cloud-deployment."Joule translates the question into a
pathBetween(fromSlug, toSlug)orconceptsForUser(userId)named query, runs it against the HANA KGE named graph, and returns an ordered tutorial sequence respecting prerequisites + the user's existing concept coverage.Strong demo narrative: "ask Joule for a learning path → see SPARQL → see KGE answer." Both SAP technologies on display.
What's already in place from Phase 1
KnowledgeGraphService.pathBetween(fromSlug, toSlug)— declared in srv/knowledge-graph-service.cds as a stub that returns[]. The contract is stable; Phase 2 fills in the implementation.KnowledgeGraphService.conceptsForUser(userId)— declared as a stub that returns{ learned: [], partial: [] }. Same contract-stable pattern.srv/lib/kg-queries.jsexportsPATH_BETWEEN_QUERYandCONCEPTS_FOR_USER_QUERYas stub SPARQL strings. Phase 2 replaces them with real queries.srv/lib/kg-sparql-client.js(sparqlQuery) — already wired, with privilege-error / syntax-error / timeout taxonomy.graphRebuildstep.getRelevantSteps/checkCodechat tools.Scope
Tasks
pathBetweenSPARQL — a multi-hop query that finds a chain of tutorials connectingfromSlugtotoSlugvia:teaches→:requiresreverse edges. SPARQL property-path syntax (?a kg:teaches/^kg:requires*/kg:teaches ?b) is the canonical shape; verify against HANA KGE's property-path support during implementation.conceptsForUserSPARQL — joins the user'sTaskRecords(completed tutorials) againstkg:teachesto computelearned(concept covered ≥1 time) andpartial(concept covered by an in-progress tutorial). Also gives the user-coverage signal that improves Phase 1'swhatToLearnNextre-ranker.findLearningPath— registered insrv/chat-service.cdsalongsidegetRelevantSteps/checkCode. CallspathBetween+conceptsForUser; respects user's existing concept coverage when ranking; returns a numbered ordered list of tutorial slugs + titles + estimated time.getRelevantStepstool (which answers "what's the next step in this tutorial").test/hybrid/kg-path-between.test.jsagainst a seeded fixture graph;test/smoke/joule-find-learning-path.test.jsagainst the deployed chat endpoint.kg.joule.path_requested,kg.joule.path_returnedevents withpathLength,fromSlug,toSlug,userIdHash. Same dispatcher pattern as the sidebar'skg.sidebar.shown.Out of scope for Phase 2
/explore/route)Design questions to resolve
pathBetweensemantics when no path exists — return empty array? Or compute a best-effort partial path through:relatedToedges? Phase 2 design decision.fromSlug(defaults to user's current position).Risks
getRelevantStepssemantics)pathBetweenreturns silly paths (e.g. via tagged-with link rather than meaningful prerequisites):teaches/:requiresonly, NOT the full ontology. Tighten with hybrid-test corpus before shippingkg-sparql-clientAcceptance criteria
KnowledgeGraphService.pathBetween('cap-getting-started', 'cap-fiori-deployment')returns a non-empty ordered array of tutorial slugsKnowledgeGraphService.conceptsForUser(userId)returns reasonablelearned/partialfor at least three real DEV usersUIEventtabledocs/superpowers/done/follows the Phase 1 templateRefs #381