Skip to content

feat: implement StudentSync v2 MVP domains - #9

Open
Ayush-Vish wants to merge 1 commit into
mainfrom
feat/student-os-mvp-5161031251550577040
Open

feat: implement StudentSync v2 MVP domains#9
Ayush-Vish wants to merge 1 commit into
mainfrom
feat/student-os-mvp-5161031251550577040

Conversation

@Ayush-Vish

@Ayush-Vish Ayush-Vish commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Implemented the 12 MVP features for "StudentSync v2" as an operating system for students.

  • Expanded the StoredUserProfile to include structured tracking for academic, projects, and profile domains.
  • Added new life domains plugins in src/core/plugins/domains/:
    • profile.ts: Life modes, Activity sessions API, Morning/Evening reflection briefs.
    • academic.ts: Study sessions, Knowledge graph tracker, Spaced repetition scheduler.
    • projects.ts: Project vault and note tracker.
  • Dynamically enabled these domains in schema.ts.

PR created automatically by Jules for task 5161031251550577040 started by @Ayush-Vish

Summary by CodeRabbit

  • New Features

    • Added new student-focused capabilities for tracking study sessions, managing projects, and updating personal preferences.
    • Users can now log activities, record interests, set a current mode, and generate morning/evening planning prompts.
    • Added support for academic review tracking, spaced-repetition reminders, and quiz-style study prompts.
    • Project management now includes creating projects, adding notes, and listing projects by status.
  • Bug Fixes

    • Improved data handling so missing student profile sections are automatically initialized instead of causing issues.

@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
scholarsync Error Error Jun 26, 2026 8:12pm

Copilot AI review requested due to automatic review settings June 26, 2026 20:12
@google-labs-jules

Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Added new academic, projects, and profile sub-stores to user profiles, extended domain config and plugin loading for those domains, and introduced tool plugins for study sessions, project tracking, life mode/interests, and activity summaries.

Changes

StudentSync v2 — Operating System for Students

Layer / File(s) Summary
Store shapes, config toggles, and design notes
src/core/store.ts, src/core/schema.ts, DESIGN_CHOICES.md
Adds new academic, projects, and profile store interfaces, extends domain config/defaults, and updates the design note for the StudentSync v2 feature set.
Domain initialization and loading
src/core/plugins/domains/_helpers.ts, src/core/plugins/domains/index.ts
Initializes the new sub-stores when missing and includes the academic, projects, and profile plugin groups when those domains are enabled.
Academic tools
src/core/plugins/domains/academic.ts
Adds study-session logging, knowledge-graph updates, spaced-repetition scheduling, and quiz-question prompt generation.
Profile tools
src/core/plugins/domains/profile.ts
Adds life-mode updates, interest edits, activity-session logging, and morning/evening summary generation.
Project tools
src/core/plugins/domains/projects.ts
Adds project creation, dated project notes, and project listing by status.

Sequence Diagram(s)

sequenceDiagram
  participant DomainConfig
  participant getLifeDomainPlugins
  participant setLifeModePlugin
  participant logStudySessionPlugin
  participant createProjectPlugin
  participant ensureDomains
  participant persist

  DomainConfig->>getLifeDomainPlugins: enable academic, projects, profile
  getLifeDomainPlugins->>setLifeModePlugin: return profile tools
  getLifeDomainPlugins->>logStudySessionPlugin: return academic tools
  getLifeDomainPlugins->>createProjectPlugin: return project tools
  setLifeModePlugin->>ensureDomains: initialize profile store
  logStudySessionPlugin->>ensureDomains: initialize academic store
  createProjectPlugin->>ensureDomains: initialize projects store
  setLifeModePlugin->>persist: persist(context.store)
  logStudySessionPlugin->>persist: persist(context.store)
  createProjectPlugin->>persist: persist(context.store)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Hop hop! New stores grew in the hay,
Study notes, project buds, and mode updates today.
I nibbled a brief, then chased the moonlight blue,
With quizzes, reflections, and carrots to review.
Thump-thump—StudentSync shines! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the StudentSync v2 MVP domains.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/student-os-mvp-5161031251550577040

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.5.0)
src/core/plugins/domains/profile.ts

File contains syntax errors that prevent linting: Line 8: expected , but instead found s; Line 8: expected , but instead found current; Line 8: expected , but instead found life; Line 8: expected , but instead found mode; Line 8: expected , but instead found .; Line 8: expected , but instead found g; Line 8: expected , but instead found .; Line 8: expected , but instead found Mode; Line 8: expected , but instead found Mode; Line 8: expected , but instead found Mode; Line 8: Expected a function body but instead found '.'.; Line 8: unterminated string literal; Line 9: expected , but instead found parameters; Line 24: expected , but instead found s; Line 24: expected , but instead found interests; Line 24: expected , but instead found graph; Line 24: expected , but instead found or; Line 24: expected , but instead found remove; Line 24: expected , but instead found interests; Line 24: Expected a function body but instead found '.'.; Line 24: unterminated string literal; Line 25: expected , but instead found parameters; Line 89: expected , but instead found s; Line 89: expected , but instead found logged; Line 89: expected , but instead found activities; Line 89: expected , but instead found and; Line 89: expected , but instead found study; Line 89: expected , but instead found sessions; Line 89: expected , but instead found .; Line 89: unterminated string literal; Line 90: expected , but instead found parameters


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.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add StudentSync v2 MVP domains (academic, projects, profile)
✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🕐 40+ Minutes

Grey Divider

Description

• Extend StoredUserProfile with academic, projects, and profile structured sub-stores.
• Add tool-based domain plugins for study tracking, project vault notes, and life-mode workflows.
• Enable new domains by default via DomainConfig and domain plugin registry wiring.
Diagram

graph TD
  A["schema.ts (DomainConfig)"] --> B["domains/index.ts"]
  B --> C["academic.ts tools"] --> F["store.ts (UserProfile)"] --> G[("store.json")]
  B --> D["projects.ts tools"] --> F
  B --> E["profile.ts tools"] --> F
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Domain registry map (data-driven wiring)
  • ➕ Avoids manual import/push blocks in domains/index.ts as domains grow
  • ➕ Makes it easier to auto-generate docs/tool lists per domain
  • ➖ Slightly more indirection than explicit imports
  • ➖ Still requires a build-time module import strategy (or dynamic import constraints)
2. Event-sourced domain updates (append-only log)
  • ➕ Better auditability and analytics over time (sessions, changes, streaks)
  • ➕ Easier to recompute derived views (e.g., mastery curves)
  • ➖ More complexity than MVP needs
  • ➖ Requires migration/compaction strategy for store growth
3. Centralized date/time helpers per domain (date-only vs timestamp)
  • ➕ Consistent filtering/aggregation (today/week/month) without string hacks
  • ➕ Less timezone ambiguity if standardized
  • ➖ Requires touching existing domains and store format decisions
  • ➖ Potential migration for already-persisted entries

Recommendation: The PR’s approach (typed sub-stores + tool plugins + config-gated registration) is appropriate for an MVP and matches existing domain patterns (finance/personal/emotional/career). As a follow-up, consider switching domains/index.ts to a small data-driven registry map to reduce wiring overhead as more domains are added; defer event sourcing/date standardization until real usage/analytics demands it.

Files changed (8) +453 / -0

Enhancement (6) +437 / -0
_helpers.tsInitialize new domain sub-stores in ensureDomains() +8/-0

Initialize new domain sub-stores in ensureDomains()

• Extends ensureDomains() to defensively create academic, projects, and profile sub-stores with sensible defaults. Ensures downstream tool plugins can assume arrays/objects exist before mutation.

src/core/plugins/domains/_helpers.ts

academic.tsAdd academic domain tools (study sessions, knowledge graph, spaced repetition, quiz) +126/-0

Add academic domain tools (study sessions, knowledge graph, spaced repetition, quiz)

• Introduces tool plugins to log study sessions (with automatic knowledge graph updates), manually update topic/subtopic mastery, compute due reviews via simple spaced repetition heuristics, and return an instruction payload for quiz generation.

src/core/plugins/domains/academic.ts

index.tsRegister academic/projects/profile plugins behind DomainConfig flags +29/-0

Register academic/projects/profile plugins behind DomainConfig flags

• Imports the new domain plugins and conditionally includes them via the existing isEnabled() gating logic. Keeps domain enablement behavior consistent: absent config defaults to enabled, explicit enabled=false disables.

src/core/plugins/domains/index.ts

profile.tsAdd profile domain tools (life mode, interests, activity sessions, briefs) +110/-0

Add profile domain tools (life mode, interests, activity sessions, briefs)

• Adds tool plugins to set the current life mode, update the interests list, log activity sessions, and generate morning/evening brief payloads based on current profile state and today's activity/study logs.

src/core/plugins/domains/profile.ts

projects.tsAdd project vault tools (create/list projects, add notes/links) +82/-0

Add project vault tools (create/list projects, add notes/links)

• Adds tool plugins to create projects, append timestamped notes (plus optional unique links) to a project by id/name, and list projects with optional status filtering.

src/core/plugins/domains/projects.ts

store.tsAdd typed stores + normalization for academic/projects/profile domains +82/-0

Add typed stores + normalization for academic/projects/profile domains

• Defines new interfaces (AcademicStore/StudySession/KnowledgeNode, ProjectsStore/ProjectEntry, ProfileStore/ActivitySession) and adds them to StoredUserProfile. Updates normalizeUserProfile() to always materialize these stores with safe defaults when loading persisted data.

src/core/store.ts

Documentation (1) +10 / -0
DESIGN_CHOICES.mdDocument StudentSync v2 MVP OS domains and tool-based API decision +10/-0

Document StudentSync v2 MVP OS domains and tool-based API decision

• Adds a new section describing the StudentSync v2 MVP feature set, the new academic/projects/profile stores, and the new domain tool plugins. Captures the rationale for using a tool-based interface rather than implementing a standalone daemon.

DESIGN_CHOICES.md

Other (1) +6 / -0
schema.tsAdd academic/projects/profile to DomainConfig and default enablement +6/-0

Add academic/projects/profile to DomainConfig and default enablement

• Extends DomainConfig with academic, projects, and profile toggles and adds them to DEFAULT_STUDENT_CONFIG as enabled by default. This makes the new domains available without additional setup.

src/core/schema.ts

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Missing getOptionalArray export 🐞 Bug ≡ Correctness
Description
The new academic/profile/projects domain plugins import getOptionalArray from
src/core/plugins/builtin/_args.js, but that module does not export it, so the process will fail
during build or at runtime with an ESM named-export error when loading these plugins.
Code

src/core/plugins/domains/academic.ts[R1-4]

+import type { ToolPlugin } from '../ToolRegistry.js';
+import type { StudySession, KnowledgeNode } from '../../store.js';
+import { getString, getOptionalString, getOptionalNumber, getOptionalArray } from '../builtin/_args.js';
+import { ensureDomains, generateId, persist, todayISO } from './_helpers.js';
Evidence
The new domain plugin files import getOptionalArray, but the only array helper exported by
_args.ts is getOptionalStringArray; therefore the import cannot be satisfied.

src/core/plugins/domains/academic.ts[1-4]
src/core/plugins/domains/profile.ts[1-4]
src/core/plugins/domains/projects.ts[1-5]
src/core/plugins/builtin/_args.ts[1-37]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several new domain plugins import `getOptionalArray` from `src/core/plugins/builtin/_args.js`, but `_args.ts` does not export such a symbol. This causes a hard failure (TypeScript compile error and/or Node ESM `does not provide an export named` error) when these modules are imported.

## Issue Context
`_args.ts` currently provides `getOptionalStringArray()` which matches the usage here (arrays of strings).

## Fix Focus Areas
- src/core/plugins/domains/academic.ts[1-70]
- src/core/plugins/domains/profile.ts[1-42]
- src/core/plugins/domains/projects.ts[1-62]
- src/core/plugins/builtin/_args.ts[1-37]

## Suggested fix
- Replace `getOptionalArray` imports/usages with `getOptionalStringArray`:
 - `subtopics`, `add`, `remove`, and `links` should be parsed via `getOptionalStringArray(args, key) ?? []` (or left `undefined` when appropriate).
- Alternatively (if you truly need a generic array parser), implement and export `getOptionalArray` from `_args.ts` and update callers to validate element types (since current callers assume `string[]`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Unvalidated durationMinutes coercion 🐞 Bug ☼ Reliability
Description
log_study_session and log_activity_session set durationMinutes via
Number(args.durationMinutes) without validation, so non-numeric or missing inputs can persist
NaN and then downstream computations (e.g. evening reflection total minutes) will return NaN.
Code

src/core/plugins/domains/profile.ts[R53-65]

+    async execute(args, context) {
+        ensureDomains(context.userProfile);
+        const session: ActivitySession = {
+            id: generateId('activity'),
+            appOrSite: getString(args, 'appOrSite'),
+            durationMinutes: Number(args.durationMinutes),
+            intent: getString(args, 'intent'),
+            date: todayISO(),
+        };
+        context.userProfile.profile!.activitySessions.push(session);
+        persist(context.store);
+        return { success: true, session };
+    },
Evidence
Both plugins coerce durationMinutes with Number(...) and later code reduces over persisted
durationMinutes, so a persisted NaN will propagate to totalStudyMinutes. A safe numeric parser
exists (getOptionalNumber) but is not used here.

src/core/plugins/domains/academic.ts[15-23]
src/core/plugins/domains/profile.ts[53-61]
src/core/plugins/domains/profile.ts[95-103]
src/core/plugins/builtin/_args.ts[16-23]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`durationMinutes` is currently coerced with `Number(...)` and persisted without checking for `undefined`/`NaN`/non-finite values. If the tool invocation bypasses schema typing (provider quirks, malformed tool args, manual invocation), this can store `NaN` and poison aggregations like `totalStudyMinutes`.

## Issue Context
You already have `getOptionalNumber()` which safely parses number-like inputs; for required numeric args you can use it plus an explicit error when it returns `undefined`.

## Fix Focus Areas
- src/core/plugins/domains/academic.ts[15-24]
- src/core/plugins/domains/profile.ts[53-61]
- src/core/plugins/builtin/_args.ts[16-23]

## Suggested fix
- In both plugins:
 - Replace `Number(args.durationMinutes)` with:
   - `const duration = getOptionalNumber(args, 'durationMinutes');`
   - `if (duration === undefined || !Number.isFinite(duration) || duration <= 0) throw new Error(...)`
   - store `durationMinutes: duration`
- (Optional) Add a `getNumber()` helper to `_args.ts` for required numeric args, mirroring `getString()` behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +1 to +4
import type { ToolPlugin } from '../ToolRegistry.js';
import type { StudySession, KnowledgeNode } from '../../store.js';
import { getString, getOptionalString, getOptionalNumber, getOptionalArray } from '../builtin/_args.js';
import { ensureDomains, generateId, persist, todayISO } from './_helpers.js';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Missing getoptionalarray export 🐞 Bug ≡ Correctness

The new academic/profile/projects domain plugins import getOptionalArray from
src/core/plugins/builtin/_args.js, but that module does not export it, so the process will fail
during build or at runtime with an ESM named-export error when loading these plugins.
Agent Prompt
## Issue description
Several new domain plugins import `getOptionalArray` from `src/core/plugins/builtin/_args.js`, but `_args.ts` does not export such a symbol. This causes a hard failure (TypeScript compile error and/or Node ESM `does not provide an export named` error) when these modules are imported.

## Issue Context
`_args.ts` currently provides `getOptionalStringArray()` which matches the usage here (arrays of strings).

## Fix Focus Areas
- src/core/plugins/domains/academic.ts[1-70]
- src/core/plugins/domains/profile.ts[1-42]
- src/core/plugins/domains/projects.ts[1-62]
- src/core/plugins/builtin/_args.ts[1-37]

## Suggested fix
- Replace `getOptionalArray` imports/usages with `getOptionalStringArray`:
  - `subtopics`, `add`, `remove`, and `links` should be parsed via `getOptionalStringArray(args, key) ?? []` (or left `undefined` when appropriate).
- Alternatively (if you truly need a generic array parser), implement and export `getOptionalArray` from `_args.ts` and update callers to validate element types (since current callers assume `string[]`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/core/plugins/domains/index.ts (1)

3-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Import the shared DomainConfig type instead of mirroring it here.

This loader still appears to carry its own DomainConfig declaration alongside src/core/schema.ts. Adding three more domain keys means the config contract now has to stay in sync in two places. Import the shared type from schema.ts so plugin loading cannot silently drift from the config schema.

Also applies to: 74-84

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/plugins/domains/index.ts` around lines 3 - 20, The domain plugin
loader is duplicating the DomainConfig contract instead of using the shared type
from schema.ts, which can drift as new domain keys are added. Update the domain
loader in domains/index.ts to import and use the shared DomainConfig type from
src/core/schema.ts, and remove any locally mirrored declaration so the plugin
loading contract stays aligned with the schema.
src/core/store.ts (1)

174-228: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

Consolidate these persisted-domain types into one source of truth.

StudySession, ProjectEntry, and ActivitySession are also declared in src/core/plugins/domains/academic.ts, src/core/plugins/domains/projects.ts, and src/core/plugins/domains/profile.ts. Keeping separate copies for the same stored shapes makes the persistence contract easy to drift. Move them to a shared types module or import them from here instead of redefining them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/store.ts` around lines 174 - 228, Consolidate the persisted domain
shapes into a single source of truth by removing the duplicate definitions of
StudySession, ProjectEntry, and ActivitySession from the domain plugin files and
importing them from the shared store types instead. Update the affected domain
modules and any related store interfaces in src/core/store.ts so AcademicStore,
ProjectsStore, and ProfileStore reference the shared types directly, keeping the
persisted contract defined in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/plugins/domains/_helpers.ts`:
- Around line 30-32: The profile initialization in ensureDomains only fills
interests and activitySessions when profile.profile already exists, but it does
not backfill a missing mode on partial profiles. Update the ensureDomains helper
in _helpers.ts to also default profile.profile.mode to the same exploration
value whenever it is absent, so this behavior matches normalizeUserProfile and
generate_morning_brief no longer sees undefined.

In `@src/core/plugins/domains/academic.ts`:
- Around line 28-36: The academic plugin `level` field is being used as both a
0-100 mastery value and a spaced-repetition interval bucket, causing
inconsistent scheduling. Update `log_study_session`, `update_knowledge_graph`,
and `get_spaced_repetition_schedule` to use a single consistent meaning, or
split the data into separate fields such as `mastery` and `reviewInterval`. Make
sure the writer in `update_knowledge_graph` and the session tracking in
`log_study_session` normalize to the same scale that
`get_spaced_repetition_schedule` reads.

In `@src/core/plugins/domains/profile.ts`:
- Line 8: The profile domain descriptions in the plugin module contain malformed
string literals because the apostrophe is written with an extra backslash,
causing the parser to see an unterminated string. Update the affected
description fields in the profile domain definition so the apostrophes are
escaped correctly or the strings use double quotes, and apply the same fix
consistently in all affected entries within the profile plugin.

In `@src/core/store.ts`:
- Around line 334-349: Deep-normalize the returned sub-stores in store.ts
instead of only checking the top-level containers. Update the AcademicStore,
ProjectsStore, and ProfileStore construction so nested records like
knowledgeGraph entries, project items, interests, and activity sessions are
individually sanitized into the shapes expected by academic.ts and projects.ts,
avoiding raw null/invalid nested fields from being passed through. Use the
existing store-building logic around AcademicStore, ProjectsStore, and
ProfileStore to locate and apply the normalization before returning these
objects.

---

Nitpick comments:
In `@src/core/plugins/domains/index.ts`:
- Around line 3-20: The domain plugin loader is duplicating the DomainConfig
contract instead of using the shared type from schema.ts, which can drift as new
domain keys are added. Update the domain loader in domains/index.ts to import
and use the shared DomainConfig type from src/core/schema.ts, and remove any
locally mirrored declaration so the plugin loading contract stays aligned with
the schema.

In `@src/core/store.ts`:
- Around line 174-228: Consolidate the persisted domain shapes into a single
source of truth by removing the duplicate definitions of StudySession,
ProjectEntry, and ActivitySession from the domain plugin files and importing
them from the shared store types instead. Update the affected domain modules and
any related store interfaces in src/core/store.ts so AcademicStore,
ProjectsStore, and ProfileStore reference the shared types directly, keeping the
persisted contract defined in one place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9e47daf-c330-48a7-a056-14aa26f449f0

📥 Commits

Reviewing files that changed from the base of the PR and between 442626b and 6dce9e7.

📒 Files selected for processing (8)
  • DESIGN_CHOICES.md
  • src/core/plugins/domains/_helpers.ts
  • src/core/plugins/domains/academic.ts
  • src/core/plugins/domains/index.ts
  • src/core/plugins/domains/profile.ts
  • src/core/plugins/domains/projects.ts
  • src/core/schema.ts
  • src/core/store.ts

Comment on lines +30 to +32
if (!profile.profile) profile.profile = { mode: 'Exploration Mode', interests: [], activitySessions: [] };
if (!profile.profile.interests) profile.profile.interests = [];
if (!profile.profile.activitySessions) profile.profile.activitySessions = [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Backfill mode when the profile store already exists.

ensureDomains only assigns the default mode when profile.profile is missing entirely. If an older or partial profile already has { interests, activitySessions } but no mode, generate_morning_brief still returns undefined after this helper runs. Default profile.profile.mode here as well so this path matches normalizeUserProfile.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/plugins/domains/_helpers.ts` around lines 30 - 32, The profile
initialization in ensureDomains only fills interests and activitySessions when
profile.profile already exists, but it does not backfill a missing mode on
partial profiles. Update the ensureDomains helper in _helpers.ts to also default
profile.profile.mode to the same exploration value whenever it is absent, so
this behavior matches normalizeUserProfile and generate_morning_brief no longer
sees undefined.

Comment on lines +28 to +36
if (!kg[session.topic]) {
kg[session.topic] = { level: 1, lastReviewed: todayISO(), subtopics: [] };
} else {
kg[session.topic].level += 0.5; // Arbitrary increment for tracking mastery
kg[session.topic].lastReviewed = todayISO();
}

persist(context.store);
return { success: true, session, currentMasteryLevel: kg[session.topic].level };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Inconsistent level semantics across academic plugins.

The level field carries three conflicting meanings:

  • log_study_session seeds new topics at level: 1 and increments by 0.5 per session (an open-ended "study count" scale).
  • update_knowledge_graph overwrites level with masteryLevel documented as 0-100.
  • get_spaced_repetition_schedule interprets level via thresholds (>10 → 30d, >5 → 14d, …).

So a user setting masteryLevel: 100 is treated as "review in 30 days", while study-session increments take ~20 sessions to reach the same bucket. The mastery (0-100) scale and the interval-threshold scale are incompatible and will produce surprising review schedules. Consider separating mastery (0-100) from the spaced-repetition level/interval, or normalizing both writers to one scale.

Also applies to: 63-66, 90-94

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/plugins/domains/academic.ts` around lines 28 - 36, The academic
plugin `level` field is being used as both a 0-100 mastery value and a
spaced-repetition interval bucket, causing inconsistent scheduling. Update
`log_study_session`, `update_knowledge_graph`, and
`get_spaced_repetition_schedule` to use a single consistent meaning, or split
the data into separate fields such as `mastery` and `reviewInterval`. Make sure
the writer in `update_knowledge_graph` and the session tracking in
`log_study_session` normalize to the same scale that
`get_spaced_repetition_schedule` reads.


export const setLifeModePlugin: ToolPlugin = {
name: 'set_life_mode',
description: 'Set the user\\'s current life mode (e.g., Exploration Mode, Exam Mode, Placement Mode).',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Syntax error: escaped single quote terminates the string literal.

These descriptions use \\' (escaped backslash followed by an unescaped quote) inside single-quoted strings, so the string closes prematurely and the file fails to parse — Biome reports "unterminated string literal" on lines 8, 24, and 89. This breaks the entire module.

Use a single backslash escape (\') or switch to double quotes.

🐛 Proposed fix (line 8 shown; apply same to 24 and 89)
-    description: 'Set the user\\'s current life mode (e.g., Exploration Mode, Exam Mode, Placement Mode).',
+    description: "Set the user's current life mode (e.g., Exploration Mode, Exam Mode, Placement Mode).",

Also applies to: 24-24, 89-89

🧰 Tools
🪛 Biome (2.5.0)

[error] 8-8: expected , but instead found s

(parse)


[error] 8-8: expected , but instead found current

(parse)


[error] 8-8: expected , but instead found life

(parse)


[error] 8-8: expected , but instead found mode

(parse)


[error] 8-8: expected , but instead found .

(parse)


[error] 8-8: expected , but instead found g

(parse)


[error] 8-8: expected , but instead found .

(parse)


[error] 8-8: expected , but instead found Mode

(parse)


[error] 8-8: expected , but instead found Mode

(parse)


[error] 8-8: expected , but instead found Mode

(parse)


[error] 8-8: Expected a function body but instead found '.'.

(parse)


[error] 8-8: unterminated string literal

(parse)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/plugins/domains/profile.ts` at line 8, The profile domain
descriptions in the plugin module contain malformed string literals because the
apostrophe is written with an extra backslash, causing the parser to see an
unterminated string. Update the affected description fields in the profile
domain definition so the apostrophes are escaped correctly or the strings use
double quotes, and apply the same fix consistently in all affected entries
within the profile plugin.

Source: Linters/SAST tools

Comment thread src/core/store.ts
Comment on lines +334 to +349

const academic: AcademicStore = {
studySessions: Array.isArray(user.academic?.studySessions) ? user.academic!.studySessions : [],
knowledgeGraph: user.academic?.knowledgeGraph && typeof user.academic.knowledgeGraph === 'object' ? user.academic.knowledgeGraph : {},
};

const projects: ProjectsStore = {
projects: Array.isArray(user.projects?.projects) ? user.projects!.projects : [],
};

const profileStore: ProfileStore = {
mode: user.profile?.mode || 'Exploration Mode',
interests: Array.isArray(user.profile?.interests) ? user.profile!.interests : [],
activitySessions: Array.isArray(user.profile?.activitySessions) ? user.profile!.activitySessions : [],
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Deep-normalize the new sub-stores before returning them.

These branches only validate the top-level container. Downstream tools assume nested entries are already well-formed — for example, academic.ts dereferences node.lastReviewed/node.subtopics, and projects.ts does project.notes.push(...) plus ...project.links. A persisted payload like { knowledgeGraph: { os: null } } or { projects: [{ notes: null, links: null }] } will survive normalization here and then crash the new plugins at runtime. Normalize each nested record here rather than passing raw objects through.

Also applies to: 367-370

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/store.ts` around lines 334 - 349, Deep-normalize the returned
sub-stores in store.ts instead of only checking the top-level containers. Update
the AcademicStore, ProjectsStore, and ProfileStore construction so nested
records like knowledgeGraph entries, project items, interests, and activity
sessions are individually sanitized into the shapes expected by academic.ts and
projects.ts, avoiding raw null/invalid nested fields from being passed through.
Use the existing store-building logic around AcademicStore, ProjectsStore, and
ProfileStore to locate and apply the normalization before returning these
objects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants