feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main) - #338

Merged
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main
Jul 15, 2026
Merged

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main)#338
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main

Conversation

@Darkest-Teddy

Copy link
Copy Markdown
Collaborator

Brings the Tutor knowledge-map rail overhaul (originally #334) to main so it deploys to staging.saplinglearn.com. #334 was merged into the stagingbranch only, which the staging site does not deploy from — so it never went live. This PR lands the same, tested feature on the trunk.

What

Replaces the old Tutor session rail (Mode / Messages / Context / Progress / Related) with the knowledge-map rail:

  • KNOWLEDGE MAP header + graph, mastery legend
  • FOCUSED CONCEPT card with an AI-generated concept description
  • IN THIS BRANCH list (mastery-colored) + ELSEWHERE IN COURSE chips
  • Graph: pan/zoom/drag now clamped to the viewport (visual design unchanged)
  • Backend: concept_describe agent + /describe-concept endpoint feeding the card

Commits (cherry-picked onto main)

  • fix(graph): clamp pan/zoom and node-drag to the viewport
  • feat(graph): concept-description agent + endpoint
  • feat(learn): rebuild the Tutor knowledge-map rail
  • fix(graph): bound inputs and handle agent failures in describe_concept
  • fix(learn): relax concept-description output cap 240 → 400 (follow-up from fix/concept-desc-cap)

Validation (local)

  • Backend: 931 passed, 1 skipped (only the pre-existing OCR asyncio-teardown error remains)
  • Frontend: tsc clean · lint 0 errors · vitest 77 passed
  • Clean cherry-pick onto main (0 conflicts)

🤖 Generated with Claude Code

Darkest-Teddyand others added 5 commits July 15, 2026 01:04
Constrain the KnowledgeGraph2D view transform and node dragging so the
graph can't be flung into empty space or clipped outside its box:
- clampView() keeps the content's bounding box inside the window (or
covering it when larger), applied on pan, wheel-zoom, and both zoom
buttons
- node drag is clamped to the visible bounds mapped back through the
current transform
Force-simulation parameters are untouched — only the camera/drag bounds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a tool-less Pydantic AI agent that returns a one-sentence,
student-facing description for a concept, plus the route that drives it:
- agents/concept_describe.py: concept_describe_agent (typed
ConceptDescription output) + build_message helper
- agents/_providers.py: register the concept_describe task on the
gemini-2.5-flash-lite tier (short single-shot generation)
- routes/graph.py: POST /api/graph/{user_id}/concept-description
Backs the Tutor knowledge-map rail's focus card for concepts that lack a
stored description (e.g. manually-added ones).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Redesign the active-session rail to the Tutor Session design and make it
a working navigator over the course map:
- rail content matches the mockup: Knowledge-map header (course code +
name), graph on a transparent radial-glow surface, tier legend, Focused
concept card, "In this branch" list, "Elsewhere in course" chips
- graph is filtered to the focused course's tree only (not the full
multi-course graph)
- focus is decoupled from the chat: clicking a node (or a list item)
focuses it in the rail without touching the conversation; the focus
card's Resume/Start button — or a double-click — switches the session
(resume existing session for that concept, else start fresh)
- focus card anchors on the course when no concept is focused
- per-concept descriptions: shown from stored data, else lazily fetched
from the concept-description endpoint and cached (skipped in local mode,
which has no AI, falling back to the connected-concepts line)
- manually add a concept (links to the focused node / course root) or
remove the focused concept; remove also hits deleteGraphNode on real
backends
- local dataset: real course_id + course codes on nodes, richer concept
lists, and one-line descriptions per concept
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The knowledge-map rail's /concept-description endpoint handed the concept
name and course label straight to the LLM with no length bound, and ran the
agent with no error handling — so an oversized payload could bloat the prompt
and any model/transport/validation failure surfaced as an opaque 500.
- Truncate concept (200) and course_label (120) before build_message.
- Wrap the agent run: (AgentRunError, httpx.HTTPError, ValidationError) -> 502,
leaving unexpected exceptions to the generic 500 handler.
- Add route tests: happy path, truncation, 502 translation, 400 empty concept,
and unexpected-exception-falls-through-to-500.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ConceptDescription.description capped at 240 chars while the prompt asks
for a ~12-28 word sentence. A verbose sentence with long technical terms
could overflow 240 -> Flashcards-style schema-validation failure -> agent
retry -> UnexpectedModelBehavior -> a user-facing 502 instead of a
slightly-long-but-valid description. Raise the ceiling to 400 (still
bounded against runaway output) and document why, so it isn't tightened
back. No test asserts the cap value.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

Copy link
Copy Markdown

Warning

Review limit reached

@Darkest-Teddy, you've reached your PR review limit, so we couldn't start this review.

Next review available in:3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 58fe91ec-a62c-4ce8-97f1-065ac1235a89

📥 Commits

Reviewing files that changed from the base of the PR and between 35136a6 and f31342c.

📒 Files selected for processing (10)
  • backend/agents/_providers.py
  • backend/agents/concept_describe.py
  • backend/routes/graph.py
  • backend/tests/test_graph_concept_description.py
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/screens/Learn.tsx
  • frontend/src/lib/api.ts
  • frontend/src/lib/data.ts
  • frontend/src/lib/localData.ts
  • frontend/src/lib/types.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tutor-rail-to-main

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf31342cCommit Preview URL

Branch Preview URL
Jul 15 2026, 05:14 AM

@Darkest-Teddy
Darkest-Teddy merged commit 502e324 into mainJul 15, 2026
6 checks passed
@Darkest-Teddy
Darkest-Teddy deleted the feat/tutor-rail-to-main branch July 15, 2026 05:16
Sign up for freeto 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.

2 participants

@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main) - #338

Merged
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main
Jul 15, 2026
Merged

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main)#338
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main

Conversation

@Darkest-Teddy

Copy link
Copy Markdown
Collaborator

Brings the Tutor knowledge-map rail overhaul (originally #334) to main so it deploys to staging.saplinglearn.com. #334 was merged into the stagingbranch only, which the staging site does not deploy from — so it never went live. This PR lands the same, tested feature on the trunk.

What

Replaces the old Tutor session rail (Mode / Messages / Context / Progress / Related) with the knowledge-map rail:

  • KNOWLEDGE MAP header + graph, mastery legend
  • FOCUSED CONCEPT card with an AI-generated concept description
  • IN THIS BRANCH list (mastery-colored) + ELSEWHERE IN COURSE chips
  • Graph: pan/zoom/drag now clamped to the viewport (visual design unchanged)
  • Backend: concept_describe agent + /describe-concept endpoint feeding the card

Commits (cherry-picked onto main)

  • fix(graph): clamp pan/zoom and node-drag to the viewport
  • feat(graph): concept-description agent + endpoint
  • feat(learn): rebuild the Tutor knowledge-map rail
  • fix(graph): bound inputs and handle agent failures in describe_concept
  • fix(learn): relax concept-description output cap 240 → 400 (follow-up from fix/concept-desc-cap)

Validation (local)

  • Backend: 931 passed, 1 skipped (only the pre-existing OCR asyncio-teardown error remains)
  • Frontend: tsc clean · lint 0 errors · vitest 77 passed
  • Clean cherry-pick onto main (0 conflicts)

🤖 Generated with Claude Code

Darkest-Teddyand others added 5 commits July 15, 2026 01:04
Constrain the KnowledgeGraph2D view transform and node dragging so the
graph can't be flung into empty space or clipped outside its box:
- clampView() keeps the content's bounding box inside the window (or
covering it when larger), applied on pan, wheel-zoom, and both zoom
buttons
- node drag is clamped to the visible bounds mapped back through the
current transform
Force-simulation parameters are untouched — only the camera/drag bounds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a tool-less Pydantic AI agent that returns a one-sentence,
student-facing description for a concept, plus the route that drives it:
- agents/concept_describe.py: concept_describe_agent (typed
ConceptDescription output) + build_message helper
- agents/_providers.py: register the concept_describe task on the
gemini-2.5-flash-lite tier (short single-shot generation)
- routes/graph.py: POST /api/graph/{user_id}/concept-description
Backs the Tutor knowledge-map rail's focus card for concepts that lack a
stored description (e.g. manually-added ones).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Redesign the active-session rail to the Tutor Session design and make it
a working navigator over the course map:
- rail content matches the mockup: Knowledge-map header (course code +
name), graph on a transparent radial-glow surface, tier legend, Focused
concept card, "In this branch" list, "Elsewhere in course" chips
- graph is filtered to the focused course's tree only (not the full
multi-course graph)
- focus is decoupled from the chat: clicking a node (or a list item)
focuses it in the rail without touching the conversation; the focus
card's Resume/Start button — or a double-click — switches the session
(resume existing session for that concept, else start fresh)
- focus card anchors on the course when no concept is focused
- per-concept descriptions: shown from stored data, else lazily fetched
from the concept-description endpoint and cached (skipped in local mode,
which has no AI, falling back to the connected-concepts line)
- manually add a concept (links to the focused node / course root) or
remove the focused concept; remove also hits deleteGraphNode on real
backends
- local dataset: real course_id + course codes on nodes, richer concept
lists, and one-line descriptions per concept
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The knowledge-map rail's /concept-description endpoint handed the concept
name and course label straight to the LLM with no length bound, and ran the
agent with no error handling — so an oversized payload could bloat the prompt
and any model/transport/validation failure surfaced as an opaque 500.
- Truncate concept (200) and course_label (120) before build_message.
- Wrap the agent run: (AgentRunError, httpx.HTTPError, ValidationError) -> 502,
leaving unexpected exceptions to the generic 500 handler.
- Add route tests: happy path, truncation, 502 translation, 400 empty concept,
and unexpected-exception-falls-through-to-500.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ConceptDescription.description capped at 240 chars while the prompt asks
for a ~12-28 word sentence. A verbose sentence with long technical terms
could overflow 240 -> Flashcards-style schema-validation failure -> agent
retry -> UnexpectedModelBehavior -> a user-facing 502 instead of a
slightly-long-but-valid description. Raise the ceiling to 400 (still
bounded against runaway output) and document why, so it isn't tightened
back. No test asserts the cap value.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

Copy link
Copy Markdown

Warning

Review limit reached

@Darkest-Teddy, you've reached your PR review limit, so we couldn't start this review.

Next review available in:3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 58fe91ec-a62c-4ce8-97f1-065ac1235a89

📥 Commits

Reviewing files that changed from the base of the PR and between 35136a6 and f31342c.

📒 Files selected for processing (10)
  • backend/agents/_providers.py
  • backend/agents/concept_describe.py
  • backend/routes/graph.py
  • backend/tests/test_graph_concept_description.py
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/screens/Learn.tsx
  • frontend/src/lib/api.ts
  • frontend/src/lib/data.ts
  • frontend/src/lib/localData.ts
  • frontend/src/lib/types.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tutor-rail-to-main

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf31342cCommit Preview URL

Branch Preview URL
Jul 15 2026, 05:14 AM

@Darkest-Teddy
Darkest-Teddy merged commit 502e324 into mainJul 15, 2026
6 checks passed
@Darkest-Teddy
Darkest-Teddy deleted the feat/tutor-rail-to-main branch July 15, 2026 05:16
Sign up for freeto 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.

2 participants

@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main) - #338

Merged
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main
Jul 15, 2026
Merged

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main)#338
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main

Conversation

@Darkest-Teddy

Copy link
Copy Markdown
Collaborator

Brings the Tutor knowledge-map rail overhaul (originally #334) to main so it deploys to staging.saplinglearn.com. #334 was merged into the stagingbranch only, which the staging site does not deploy from — so it never went live. This PR lands the same, tested feature on the trunk.

What

Replaces the old Tutor session rail (Mode / Messages / Context / Progress / Related) with the knowledge-map rail:

  • KNOWLEDGE MAP header + graph, mastery legend
  • FOCUSED CONCEPT card with an AI-generated concept description
  • IN THIS BRANCH list (mastery-colored) + ELSEWHERE IN COURSE chips
  • Graph: pan/zoom/drag now clamped to the viewport (visual design unchanged)
  • Backend: concept_describe agent + /describe-concept endpoint feeding the card

Commits (cherry-picked onto main)

  • fix(graph): clamp pan/zoom and node-drag to the viewport
  • feat(graph): concept-description agent + endpoint
  • feat(learn): rebuild the Tutor knowledge-map rail
  • fix(graph): bound inputs and handle agent failures in describe_concept
  • fix(learn): relax concept-description output cap 240 → 400 (follow-up from fix/concept-desc-cap)

Validation (local)

  • Backend: 931 passed, 1 skipped (only the pre-existing OCR asyncio-teardown error remains)
  • Frontend: tsc clean · lint 0 errors · vitest 77 passed
  • Clean cherry-pick onto main (0 conflicts)

🤖 Generated with Claude Code

Darkest-Teddyand others added 5 commits July 15, 2026 01:04
Constrain the KnowledgeGraph2D view transform and node dragging so the
graph can't be flung into empty space or clipped outside its box:
- clampView() keeps the content's bounding box inside the window (or
covering it when larger), applied on pan, wheel-zoom, and both zoom
buttons
- node drag is clamped to the visible bounds mapped back through the
current transform
Force-simulation parameters are untouched — only the camera/drag bounds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a tool-less Pydantic AI agent that returns a one-sentence,
student-facing description for a concept, plus the route that drives it:
- agents/concept_describe.py: concept_describe_agent (typed
ConceptDescription output) + build_message helper
- agents/_providers.py: register the concept_describe task on the
gemini-2.5-flash-lite tier (short single-shot generation)
- routes/graph.py: POST /api/graph/{user_id}/concept-description
Backs the Tutor knowledge-map rail's focus card for concepts that lack a
stored description (e.g. manually-added ones).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Redesign the active-session rail to the Tutor Session design and make it
a working navigator over the course map:
- rail content matches the mockup: Knowledge-map header (course code +
name), graph on a transparent radial-glow surface, tier legend, Focused
concept card, "In this branch" list, "Elsewhere in course" chips
- graph is filtered to the focused course's tree only (not the full
multi-course graph)
- focus is decoupled from the chat: clicking a node (or a list item)
focuses it in the rail without touching the conversation; the focus
card's Resume/Start button — or a double-click — switches the session
(resume existing session for that concept, else start fresh)
- focus card anchors on the course when no concept is focused
- per-concept descriptions: shown from stored data, else lazily fetched
from the concept-description endpoint and cached (skipped in local mode,
which has no AI, falling back to the connected-concepts line)
- manually add a concept (links to the focused node / course root) or
remove the focused concept; remove also hits deleteGraphNode on real
backends
- local dataset: real course_id + course codes on nodes, richer concept
lists, and one-line descriptions per concept
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The knowledge-map rail's /concept-description endpoint handed the concept
name and course label straight to the LLM with no length bound, and ran the
agent with no error handling — so an oversized payload could bloat the prompt
and any model/transport/validation failure surfaced as an opaque 500.
- Truncate concept (200) and course_label (120) before build_message.
- Wrap the agent run: (AgentRunError, httpx.HTTPError, ValidationError) -> 502,
leaving unexpected exceptions to the generic 500 handler.
- Add route tests: happy path, truncation, 502 translation, 400 empty concept,
and unexpected-exception-falls-through-to-500.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ConceptDescription.description capped at 240 chars while the prompt asks
for a ~12-28 word sentence. A verbose sentence with long technical terms
could overflow 240 -> Flashcards-style schema-validation failure -> agent
retry -> UnexpectedModelBehavior -> a user-facing 502 instead of a
slightly-long-but-valid description. Raise the ceiling to 400 (still
bounded against runaway output) and document why, so it isn't tightened
back. No test asserts the cap value.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

Copy link
Copy Markdown

Warning

Review limit reached

@Darkest-Teddy, you've reached your PR review limit, so we couldn't start this review.

Next review available in:3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 58fe91ec-a62c-4ce8-97f1-065ac1235a89

📥 Commits

Reviewing files that changed from the base of the PR and between 35136a6 and f31342c.

📒 Files selected for processing (10)
  • backend/agents/_providers.py
  • backend/agents/concept_describe.py
  • backend/routes/graph.py
  • backend/tests/test_graph_concept_description.py
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/screens/Learn.tsx
  • frontend/src/lib/api.ts
  • frontend/src/lib/data.ts
  • frontend/src/lib/localData.ts
  • frontend/src/lib/types.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tutor-rail-to-main

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf31342cCommit Preview URL

Branch Preview URL
Jul 15 2026, 05:14 AM

@Darkest-Teddy
Darkest-Teddy merged commit 502e324 into mainJul 15, 2026
6 checks passed
@Darkest-Teddy
Darkest-Teddy deleted the feat/tutor-rail-to-main branch July 15, 2026 05:16
Sign up for freeto 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.

2 participants

@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main) - #338

Merged
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main
Jul 15, 2026
Merged

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main)#338
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main

Conversation

@Darkest-Teddy

Copy link
Copy Markdown
Collaborator

Brings the Tutor knowledge-map rail overhaul (originally #334) to main so it deploys to staging.saplinglearn.com. #334 was merged into the stagingbranch only, which the staging site does not deploy from — so it never went live. This PR lands the same, tested feature on the trunk.

What

Replaces the old Tutor session rail (Mode / Messages / Context / Progress / Related) with the knowledge-map rail:

  • KNOWLEDGE MAP header + graph, mastery legend
  • FOCUSED CONCEPT card with an AI-generated concept description
  • IN THIS BRANCH list (mastery-colored) + ELSEWHERE IN COURSE chips
  • Graph: pan/zoom/drag now clamped to the viewport (visual design unchanged)
  • Backend: concept_describe agent + /describe-concept endpoint feeding the card

Commits (cherry-picked onto main)

  • fix(graph): clamp pan/zoom and node-drag to the viewport
  • feat(graph): concept-description agent + endpoint
  • feat(learn): rebuild the Tutor knowledge-map rail
  • fix(graph): bound inputs and handle agent failures in describe_concept
  • fix(learn): relax concept-description output cap 240 → 400 (follow-up from fix/concept-desc-cap)

Validation (local)

  • Backend: 931 passed, 1 skipped (only the pre-existing OCR asyncio-teardown error remains)
  • Frontend: tsc clean · lint 0 errors · vitest 77 passed
  • Clean cherry-pick onto main (0 conflicts)

🤖 Generated with Claude Code

Darkest-Teddyand others added 5 commits July 15, 2026 01:04
Constrain the KnowledgeGraph2D view transform and node dragging so the
graph can't be flung into empty space or clipped outside its box:
- clampView() keeps the content's bounding box inside the window (or
covering it when larger), applied on pan, wheel-zoom, and both zoom
buttons
- node drag is clamped to the visible bounds mapped back through the
current transform
Force-simulation parameters are untouched — only the camera/drag bounds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a tool-less Pydantic AI agent that returns a one-sentence,
student-facing description for a concept, plus the route that drives it:
- agents/concept_describe.py: concept_describe_agent (typed
ConceptDescription output) + build_message helper
- agents/_providers.py: register the concept_describe task on the
gemini-2.5-flash-lite tier (short single-shot generation)
- routes/graph.py: POST /api/graph/{user_id}/concept-description
Backs the Tutor knowledge-map rail's focus card for concepts that lack a
stored description (e.g. manually-added ones).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Redesign the active-session rail to the Tutor Session design and make it
a working navigator over the course map:
- rail content matches the mockup: Knowledge-map header (course code +
name), graph on a transparent radial-glow surface, tier legend, Focused
concept card, "In this branch" list, "Elsewhere in course" chips
- graph is filtered to the focused course's tree only (not the full
multi-course graph)
- focus is decoupled from the chat: clicking a node (or a list item)
focuses it in the rail without touching the conversation; the focus
card's Resume/Start button — or a double-click — switches the session
(resume existing session for that concept, else start fresh)
- focus card anchors on the course when no concept is focused
- per-concept descriptions: shown from stored data, else lazily fetched
from the concept-description endpoint and cached (skipped in local mode,
which has no AI, falling back to the connected-concepts line)
- manually add a concept (links to the focused node / course root) or
remove the focused concept; remove also hits deleteGraphNode on real
backends
- local dataset: real course_id + course codes on nodes, richer concept
lists, and one-line descriptions per concept
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The knowledge-map rail's /concept-description endpoint handed the concept
name and course label straight to the LLM with no length bound, and ran the
agent with no error handling — so an oversized payload could bloat the prompt
and any model/transport/validation failure surfaced as an opaque 500.
- Truncate concept (200) and course_label (120) before build_message.
- Wrap the agent run: (AgentRunError, httpx.HTTPError, ValidationError) -> 502,
leaving unexpected exceptions to the generic 500 handler.
- Add route tests: happy path, truncation, 502 translation, 400 empty concept,
and unexpected-exception-falls-through-to-500.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ConceptDescription.description capped at 240 chars while the prompt asks
for a ~12-28 word sentence. A verbose sentence with long technical terms
could overflow 240 -> Flashcards-style schema-validation failure -> agent
retry -> UnexpectedModelBehavior -> a user-facing 502 instead of a
slightly-long-but-valid description. Raise the ceiling to 400 (still
bounded against runaway output) and document why, so it isn't tightened
back. No test asserts the cap value.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

Copy link
Copy Markdown

Warning

Review limit reached

@Darkest-Teddy, you've reached your PR review limit, so we couldn't start this review.

Next review available in:3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 58fe91ec-a62c-4ce8-97f1-065ac1235a89

📥 Commits

Reviewing files that changed from the base of the PR and between 35136a6 and f31342c.

📒 Files selected for processing (10)
  • backend/agents/_providers.py
  • backend/agents/concept_describe.py
  • backend/routes/graph.py
  • backend/tests/test_graph_concept_description.py
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/screens/Learn.tsx
  • frontend/src/lib/api.ts
  • frontend/src/lib/data.ts
  • frontend/src/lib/localData.ts
  • frontend/src/lib/types.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tutor-rail-to-main

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf31342cCommit Preview URL

Branch Preview URL
Jul 15 2026, 05:14 AM

@Darkest-Teddy
Darkest-Teddy merged commit 502e324 into mainJul 15, 2026
6 checks passed
@Darkest-Teddy
Darkest-Teddy deleted the feat/tutor-rail-to-main branch July 15, 2026 05:16
Sign up for freeto 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.

2 participants

@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main) - #338

Merged
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main
Jul 15, 2026
Merged

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main)#338
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main

Conversation

@Darkest-Teddy

Copy link
Copy Markdown
Collaborator

Brings the Tutor knowledge-map rail overhaul (originally #334) to main so it deploys to staging.saplinglearn.com. #334 was merged into the stagingbranch only, which the staging site does not deploy from — so it never went live. This PR lands the same, tested feature on the trunk.

What

Replaces the old Tutor session rail (Mode / Messages / Context / Progress / Related) with the knowledge-map rail:

  • KNOWLEDGE MAP header + graph, mastery legend
  • FOCUSED CONCEPT card with an AI-generated concept description
  • IN THIS BRANCH list (mastery-colored) + ELSEWHERE IN COURSE chips
  • Graph: pan/zoom/drag now clamped to the viewport (visual design unchanged)
  • Backend: concept_describe agent + /describe-concept endpoint feeding the card

Commits (cherry-picked onto main)

  • fix(graph): clamp pan/zoom and node-drag to the viewport
  • feat(graph): concept-description agent + endpoint
  • feat(learn): rebuild the Tutor knowledge-map rail
  • fix(graph): bound inputs and handle agent failures in describe_concept
  • fix(learn): relax concept-description output cap 240 → 400 (follow-up from fix/concept-desc-cap)

Validation (local)

  • Backend: 931 passed, 1 skipped (only the pre-existing OCR asyncio-teardown error remains)
  • Frontend: tsc clean · lint 0 errors · vitest 77 passed
  • Clean cherry-pick onto main (0 conflicts)

🤖 Generated with Claude Code

Darkest-Teddyand others added 5 commits July 15, 2026 01:04
Constrain the KnowledgeGraph2D view transform and node dragging so the
graph can't be flung into empty space or clipped outside its box:
- clampView() keeps the content's bounding box inside the window (or
covering it when larger), applied on pan, wheel-zoom, and both zoom
buttons
- node drag is clamped to the visible bounds mapped back through the
current transform
Force-simulation parameters are untouched — only the camera/drag bounds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a tool-less Pydantic AI agent that returns a one-sentence,
student-facing description for a concept, plus the route that drives it:
- agents/concept_describe.py: concept_describe_agent (typed
ConceptDescription output) + build_message helper
- agents/_providers.py: register the concept_describe task on the
gemini-2.5-flash-lite tier (short single-shot generation)
- routes/graph.py: POST /api/graph/{user_id}/concept-description
Backs the Tutor knowledge-map rail's focus card for concepts that lack a
stored description (e.g. manually-added ones).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Redesign the active-session rail to the Tutor Session design and make it
a working navigator over the course map:
- rail content matches the mockup: Knowledge-map header (course code +
name), graph on a transparent radial-glow surface, tier legend, Focused
concept card, "In this branch" list, "Elsewhere in course" chips
- graph is filtered to the focused course's tree only (not the full
multi-course graph)
- focus is decoupled from the chat: clicking a node (or a list item)
focuses it in the rail without touching the conversation; the focus
card's Resume/Start button — or a double-click — switches the session
(resume existing session for that concept, else start fresh)
- focus card anchors on the course when no concept is focused
- per-concept descriptions: shown from stored data, else lazily fetched
from the concept-description endpoint and cached (skipped in local mode,
which has no AI, falling back to the connected-concepts line)
- manually add a concept (links to the focused node / course root) or
remove the focused concept; remove also hits deleteGraphNode on real
backends
- local dataset: real course_id + course codes on nodes, richer concept
lists, and one-line descriptions per concept
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The knowledge-map rail's /concept-description endpoint handed the concept
name and course label straight to the LLM with no length bound, and ran the
agent with no error handling — so an oversized payload could bloat the prompt
and any model/transport/validation failure surfaced as an opaque 500.
- Truncate concept (200) and course_label (120) before build_message.
- Wrap the agent run: (AgentRunError, httpx.HTTPError, ValidationError) -> 502,
leaving unexpected exceptions to the generic 500 handler.
- Add route tests: happy path, truncation, 502 translation, 400 empty concept,
and unexpected-exception-falls-through-to-500.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ConceptDescription.description capped at 240 chars while the prompt asks
for a ~12-28 word sentence. A verbose sentence with long technical terms
could overflow 240 -> Flashcards-style schema-validation failure -> agent
retry -> UnexpectedModelBehavior -> a user-facing 502 instead of a
slightly-long-but-valid description. Raise the ceiling to 400 (still
bounded against runaway output) and document why, so it isn't tightened
back. No test asserts the cap value.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

Copy link
Copy Markdown

Warning

Review limit reached

@Darkest-Teddy, you've reached your PR review limit, so we couldn't start this review.

Next review available in:3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 58fe91ec-a62c-4ce8-97f1-065ac1235a89

📥 Commits

Reviewing files that changed from the base of the PR and between 35136a6 and f31342c.

📒 Files selected for processing (10)
  • backend/agents/_providers.py
  • backend/agents/concept_describe.py
  • backend/routes/graph.py
  • backend/tests/test_graph_concept_description.py
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/screens/Learn.tsx
  • frontend/src/lib/api.ts
  • frontend/src/lib/data.ts
  • frontend/src/lib/localData.ts
  • frontend/src/lib/types.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tutor-rail-to-main

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf31342cCommit Preview URL

Branch Preview URL
Jul 15 2026, 05:14 AM

@Darkest-Teddy
Darkest-Teddy merged commit 502e324 into mainJul 15, 2026
6 checks passed
@Darkest-Teddy
Darkest-Teddy deleted the feat/tutor-rail-to-main branch July 15, 2026 05:16
Sign up for freeto 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.

2 participants

@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main) - #338

Merged
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main
Jul 15, 2026
Merged

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main)#338
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main

Conversation

@Darkest-Teddy

Copy link
Copy Markdown
Collaborator

Brings the Tutor knowledge-map rail overhaul (originally #334) to main so it deploys to staging.saplinglearn.com. #334 was merged into the stagingbranch only, which the staging site does not deploy from — so it never went live. This PR lands the same, tested feature on the trunk.

What

Replaces the old Tutor session rail (Mode / Messages / Context / Progress / Related) with the knowledge-map rail:

  • KNOWLEDGE MAP header + graph, mastery legend
  • FOCUSED CONCEPT card with an AI-generated concept description
  • IN THIS BRANCH list (mastery-colored) + ELSEWHERE IN COURSE chips
  • Graph: pan/zoom/drag now clamped to the viewport (visual design unchanged)
  • Backend: concept_describe agent + /describe-concept endpoint feeding the card

Commits (cherry-picked onto main)

  • fix(graph): clamp pan/zoom and node-drag to the viewport
  • feat(graph): concept-description agent + endpoint
  • feat(learn): rebuild the Tutor knowledge-map rail
  • fix(graph): bound inputs and handle agent failures in describe_concept
  • fix(learn): relax concept-description output cap 240 → 400 (follow-up from fix/concept-desc-cap)

Validation (local)

  • Backend: 931 passed, 1 skipped (only the pre-existing OCR asyncio-teardown error remains)
  • Frontend: tsc clean · lint 0 errors · vitest 77 passed
  • Clean cherry-pick onto main (0 conflicts)

🤖 Generated with Claude Code

Darkest-Teddyand others added 5 commits July 15, 2026 01:04
Constrain the KnowledgeGraph2D view transform and node dragging so the
graph can't be flung into empty space or clipped outside its box:
- clampView() keeps the content's bounding box inside the window (or
covering it when larger), applied on pan, wheel-zoom, and both zoom
buttons
- node drag is clamped to the visible bounds mapped back through the
current transform
Force-simulation parameters are untouched — only the camera/drag bounds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a tool-less Pydantic AI agent that returns a one-sentence,
student-facing description for a concept, plus the route that drives it:
- agents/concept_describe.py: concept_describe_agent (typed
ConceptDescription output) + build_message helper
- agents/_providers.py: register the concept_describe task on the
gemini-2.5-flash-lite tier (short single-shot generation)
- routes/graph.py: POST /api/graph/{user_id}/concept-description
Backs the Tutor knowledge-map rail's focus card for concepts that lack a
stored description (e.g. manually-added ones).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Redesign the active-session rail to the Tutor Session design and make it
a working navigator over the course map:
- rail content matches the mockup: Knowledge-map header (course code +
name), graph on a transparent radial-glow surface, tier legend, Focused
concept card, "In this branch" list, "Elsewhere in course" chips
- graph is filtered to the focused course's tree only (not the full
multi-course graph)
- focus is decoupled from the chat: clicking a node (or a list item)
focuses it in the rail without touching the conversation; the focus
card's Resume/Start button — or a double-click — switches the session
(resume existing session for that concept, else start fresh)
- focus card anchors on the course when no concept is focused
- per-concept descriptions: shown from stored data, else lazily fetched
from the concept-description endpoint and cached (skipped in local mode,
which has no AI, falling back to the connected-concepts line)
- manually add a concept (links to the focused node / course root) or
remove the focused concept; remove also hits deleteGraphNode on real
backends
- local dataset: real course_id + course codes on nodes, richer concept
lists, and one-line descriptions per concept
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The knowledge-map rail's /concept-description endpoint handed the concept
name and course label straight to the LLM with no length bound, and ran the
agent with no error handling — so an oversized payload could bloat the prompt
and any model/transport/validation failure surfaced as an opaque 500.
- Truncate concept (200) and course_label (120) before build_message.
- Wrap the agent run: (AgentRunError, httpx.HTTPError, ValidationError) -> 502,
leaving unexpected exceptions to the generic 500 handler.
- Add route tests: happy path, truncation, 502 translation, 400 empty concept,
and unexpected-exception-falls-through-to-500.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ConceptDescription.description capped at 240 chars while the prompt asks
for a ~12-28 word sentence. A verbose sentence with long technical terms
could overflow 240 -> Flashcards-style schema-validation failure -> agent
retry -> UnexpectedModelBehavior -> a user-facing 502 instead of a
slightly-long-but-valid description. Raise the ceiling to 400 (still
bounded against runaway output) and document why, so it isn't tightened
back. No test asserts the cap value.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

Copy link
Copy Markdown

Warning

Review limit reached

@Darkest-Teddy, you've reached your PR review limit, so we couldn't start this review.

Next review available in:3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 58fe91ec-a62c-4ce8-97f1-065ac1235a89

📥 Commits

Reviewing files that changed from the base of the PR and between 35136a6 and f31342c.

📒 Files selected for processing (10)
  • backend/agents/_providers.py
  • backend/agents/concept_describe.py
  • backend/routes/graph.py
  • backend/tests/test_graph_concept_description.py
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/screens/Learn.tsx
  • frontend/src/lib/api.ts
  • frontend/src/lib/data.ts
  • frontend/src/lib/localData.ts
  • frontend/src/lib/types.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tutor-rail-to-main

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf31342cCommit Preview URL

Branch Preview URL
Jul 15 2026, 05:14 AM

@Darkest-Teddy
Darkest-Teddy merged commit 502e324 into mainJul 15, 2026
6 checks passed
@Darkest-Teddy
Darkest-Teddy deleted the feat/tutor-rail-to-main branch July 15, 2026 05:16
Sign up for freeto 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.

2 participants

@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main) - #338

Merged
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main
Jul 15, 2026
Merged

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main)#338
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main

Conversation

@Darkest-Teddy

Copy link
Copy Markdown
Collaborator

Brings the Tutor knowledge-map rail overhaul (originally #334) to main so it deploys to staging.saplinglearn.com. #334 was merged into the stagingbranch only, which the staging site does not deploy from — so it never went live. This PR lands the same, tested feature on the trunk.

What

Replaces the old Tutor session rail (Mode / Messages / Context / Progress / Related) with the knowledge-map rail:

  • KNOWLEDGE MAP header + graph, mastery legend
  • FOCUSED CONCEPT card with an AI-generated concept description
  • IN THIS BRANCH list (mastery-colored) + ELSEWHERE IN COURSE chips
  • Graph: pan/zoom/drag now clamped to the viewport (visual design unchanged)
  • Backend: concept_describe agent + /describe-concept endpoint feeding the card

Commits (cherry-picked onto main)

  • fix(graph): clamp pan/zoom and node-drag to the viewport
  • feat(graph): concept-description agent + endpoint
  • feat(learn): rebuild the Tutor knowledge-map rail
  • fix(graph): bound inputs and handle agent failures in describe_concept
  • fix(learn): relax concept-description output cap 240 → 400 (follow-up from fix/concept-desc-cap)

Validation (local)

  • Backend: 931 passed, 1 skipped (only the pre-existing OCR asyncio-teardown error remains)
  • Frontend: tsc clean · lint 0 errors · vitest 77 passed
  • Clean cherry-pick onto main (0 conflicts)

🤖 Generated with Claude Code

Darkest-Teddyand others added 5 commits July 15, 2026 01:04
Constrain the KnowledgeGraph2D view transform and node dragging so the
graph can't be flung into empty space or clipped outside its box:
- clampView() keeps the content's bounding box inside the window (or
covering it when larger), applied on pan, wheel-zoom, and both zoom
buttons
- node drag is clamped to the visible bounds mapped back through the
current transform
Force-simulation parameters are untouched — only the camera/drag bounds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a tool-less Pydantic AI agent that returns a one-sentence,
student-facing description for a concept, plus the route that drives it:
- agents/concept_describe.py: concept_describe_agent (typed
ConceptDescription output) + build_message helper
- agents/_providers.py: register the concept_describe task on the
gemini-2.5-flash-lite tier (short single-shot generation)
- routes/graph.py: POST /api/graph/{user_id}/concept-description
Backs the Tutor knowledge-map rail's focus card for concepts that lack a
stored description (e.g. manually-added ones).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Redesign the active-session rail to the Tutor Session design and make it
a working navigator over the course map:
- rail content matches the mockup: Knowledge-map header (course code +
name), graph on a transparent radial-glow surface, tier legend, Focused
concept card, "In this branch" list, "Elsewhere in course" chips
- graph is filtered to the focused course's tree only (not the full
multi-course graph)
- focus is decoupled from the chat: clicking a node (or a list item)
focuses it in the rail without touching the conversation; the focus
card's Resume/Start button — or a double-click — switches the session
(resume existing session for that concept, else start fresh)
- focus card anchors on the course when no concept is focused
- per-concept descriptions: shown from stored data, else lazily fetched
from the concept-description endpoint and cached (skipped in local mode,
which has no AI, falling back to the connected-concepts line)
- manually add a concept (links to the focused node / course root) or
remove the focused concept; remove also hits deleteGraphNode on real
backends
- local dataset: real course_id + course codes on nodes, richer concept
lists, and one-line descriptions per concept
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The knowledge-map rail's /concept-description endpoint handed the concept
name and course label straight to the LLM with no length bound, and ran the
agent with no error handling — so an oversized payload could bloat the prompt
and any model/transport/validation failure surfaced as an opaque 500.
- Truncate concept (200) and course_label (120) before build_message.
- Wrap the agent run: (AgentRunError, httpx.HTTPError, ValidationError) -> 502,
leaving unexpected exceptions to the generic 500 handler.
- Add route tests: happy path, truncation, 502 translation, 400 empty concept,
and unexpected-exception-falls-through-to-500.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ConceptDescription.description capped at 240 chars while the prompt asks
for a ~12-28 word sentence. A verbose sentence with long technical terms
could overflow 240 -> Flashcards-style schema-validation failure -> agent
retry -> UnexpectedModelBehavior -> a user-facing 502 instead of a
slightly-long-but-valid description. Raise the ceiling to 400 (still
bounded against runaway output) and document why, so it isn't tightened
back. No test asserts the cap value.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

Copy link
Copy Markdown

Warning

Review limit reached

@Darkest-Teddy, you've reached your PR review limit, so we couldn't start this review.

Next review available in:3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 58fe91ec-a62c-4ce8-97f1-065ac1235a89

📥 Commits

Reviewing files that changed from the base of the PR and between 35136a6 and f31342c.

📒 Files selected for processing (10)
  • backend/agents/_providers.py
  • backend/agents/concept_describe.py
  • backend/routes/graph.py
  • backend/tests/test_graph_concept_description.py
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/screens/Learn.tsx
  • frontend/src/lib/api.ts
  • frontend/src/lib/data.ts
  • frontend/src/lib/localData.ts
  • frontend/src/lib/types.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tutor-rail-to-main

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf31342cCommit Preview URL

Branch Preview URL
Jul 15 2026, 05:14 AM

@Darkest-Teddy
Darkest-Teddy merged commit 502e324 into mainJul 15, 2026
6 checks passed
@Darkest-Teddy
Darkest-Teddy deleted the feat/tutor-rail-to-main branch July 15, 2026 05:16
Sign up for freeto 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.

2 participants

@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main) - #338

Merged
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main
Jul 15, 2026
Merged

feat(learn): Tutor knowledge-map rail overhaul + AI concept descriptions (→ main)#338
Darkest-Teddy merged 5 commits into
mainfrom
feat/tutor-rail-to-main

Conversation

@Darkest-Teddy

Copy link
Copy Markdown
Collaborator

Brings the Tutor knowledge-map rail overhaul (originally #334) to main so it deploys to staging.saplinglearn.com. #334 was merged into the stagingbranch only, which the staging site does not deploy from — so it never went live. This PR lands the same, tested feature on the trunk.

What

Replaces the old Tutor session rail (Mode / Messages / Context / Progress / Related) with the knowledge-map rail:

  • KNOWLEDGE MAP header + graph, mastery legend
  • FOCUSED CONCEPT card with an AI-generated concept description
  • IN THIS BRANCH list (mastery-colored) + ELSEWHERE IN COURSE chips
  • Graph: pan/zoom/drag now clamped to the viewport (visual design unchanged)
  • Backend: concept_describe agent + /describe-concept endpoint feeding the card

Commits (cherry-picked onto main)

  • fix(graph): clamp pan/zoom and node-drag to the viewport
  • feat(graph): concept-description agent + endpoint
  • feat(learn): rebuild the Tutor knowledge-map rail
  • fix(graph): bound inputs and handle agent failures in describe_concept
  • fix(learn): relax concept-description output cap 240 → 400 (follow-up from fix/concept-desc-cap)

Validation (local)

  • Backend: 931 passed, 1 skipped (only the pre-existing OCR asyncio-teardown error remains)
  • Frontend: tsc clean · lint 0 errors · vitest 77 passed
  • Clean cherry-pick onto main (0 conflicts)

🤖 Generated with Claude Code

Darkest-Teddyand others added 5 commits July 15, 2026 01:04
Constrain the KnowledgeGraph2D view transform and node dragging so the
graph can't be flung into empty space or clipped outside its box:
- clampView() keeps the content's bounding box inside the window (or
covering it when larger), applied on pan, wheel-zoom, and both zoom
buttons
- node drag is clamped to the visible bounds mapped back through the
current transform
Force-simulation parameters are untouched — only the camera/drag bounds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a tool-less Pydantic AI agent that returns a one-sentence,
student-facing description for a concept, plus the route that drives it:
- agents/concept_describe.py: concept_describe_agent (typed
ConceptDescription output) + build_message helper
- agents/_providers.py: register the concept_describe task on the
gemini-2.5-flash-lite tier (short single-shot generation)
- routes/graph.py: POST /api/graph/{user_id}/concept-description
Backs the Tutor knowledge-map rail's focus card for concepts that lack a
stored description (e.g. manually-added ones).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Redesign the active-session rail to the Tutor Session design and make it
a working navigator over the course map:
- rail content matches the mockup: Knowledge-map header (course code +
name), graph on a transparent radial-glow surface, tier legend, Focused
concept card, "In this branch" list, "Elsewhere in course" chips
- graph is filtered to the focused course's tree only (not the full
multi-course graph)
- focus is decoupled from the chat: clicking a node (or a list item)
focuses it in the rail without touching the conversation; the focus
card's Resume/Start button — or a double-click — switches the session
(resume existing session for that concept, else start fresh)
- focus card anchors on the course when no concept is focused
- per-concept descriptions: shown from stored data, else lazily fetched
from the concept-description endpoint and cached (skipped in local mode,
which has no AI, falling back to the connected-concepts line)
- manually add a concept (links to the focused node / course root) or
remove the focused concept; remove also hits deleteGraphNode on real
backends
- local dataset: real course_id + course codes on nodes, richer concept
lists, and one-line descriptions per concept
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The knowledge-map rail's /concept-description endpoint handed the concept
name and course label straight to the LLM with no length bound, and ran the
agent with no error handling — so an oversized payload could bloat the prompt
and any model/transport/validation failure surfaced as an opaque 500.
- Truncate concept (200) and course_label (120) before build_message.
- Wrap the agent run: (AgentRunError, httpx.HTTPError, ValidationError) -> 502,
leaving unexpected exceptions to the generic 500 handler.
- Add route tests: happy path, truncation, 502 translation, 400 empty concept,
and unexpected-exception-falls-through-to-500.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ConceptDescription.description capped at 240 chars while the prompt asks
for a ~12-28 word sentence. A verbose sentence with long technical terms
could overflow 240 -> Flashcards-style schema-validation failure -> agent
retry -> UnexpectedModelBehavior -> a user-facing 502 instead of a
slightly-long-but-valid description. Raise the ceiling to 400 (still
bounded against runaway output) and document why, so it isn't tightened
back. No test asserts the cap value.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

Copy link
Copy Markdown

Warning

Review limit reached

@Darkest-Teddy, you've reached your PR review limit, so we couldn't start this review.

Next review available in:3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 58fe91ec-a62c-4ce8-97f1-065ac1235a89

📥 Commits

Reviewing files that changed from the base of the PR and between 35136a6 and f31342c.

📒 Files selected for processing (10)
  • backend/agents/_providers.py
  • backend/agents/concept_describe.py
  • backend/routes/graph.py
  • backend/tests/test_graph_concept_description.py
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/screens/Learn.tsx
  • frontend/src/lib/api.ts
  • frontend/src/lib/data.ts
  • frontend/src/lib/localData.ts
  • frontend/src/lib/types.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tutor-rail-to-main

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf31342cCommit Preview URL

Branch Preview URL
Jul 15 2026, 05:14 AM

@Darkest-Teddy
Darkest-Teddy merged commit 502e324 into mainJul 15, 2026
6 checks passed
@Darkest-Teddy
Darkest-Teddy deleted the feat/tutor-rail-to-main branch July 15, 2026 05:16
Sign up for freeto 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.

2 participants

@Darkest-Teddy@Jose-Gael-Cruz-Lopez