debug: add comprehensive logging to trace REINFORCEMENT_CONFIG import issue
Adding logging at: 1. fluency-thresholds.ts module load time (before and after exports) 2. progress-manager.ts import time (to see what was imported) This will help identify if: - The module isn't loading at all - The config object is partially defined - There's a circular dependency timing issue 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,153 +1,31 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"WebFetch(domain:github.com)",
|
||||
"WebFetch(domain:react-resizable-panels.vercel.app)",
|
||||
"Bash(gh run watch:*)",
|
||||
"Bash(npm run build:*)",
|
||||
"Bash(NODE_ENV=production npm run build:*)",
|
||||
"Bash(npx @pandacss/dev:*)",
|
||||
"Bash(npm run build-storybook:*)",
|
||||
"Bash(ssh nas.home.network:*)",
|
||||
"Bash(python3:*)",
|
||||
"Bash(curl:*)",
|
||||
"WebSearch",
|
||||
"WebFetch(domain:community.home-assistant.io)",
|
||||
"WebFetch(domain:raw.githubusercontent.com)",
|
||||
"WebFetch(domain:www.google.com)",
|
||||
"Bash(gcloud auth list:*)",
|
||||
"Bash(gcloud auth login:*)",
|
||||
"Bash(gcloud projects list:*)",
|
||||
"Bash(gcloud projects create:*)",
|
||||
"Bash(gcloud config set:*)",
|
||||
"Bash(gcloud services enable:*)",
|
||||
"Bash(gcloud alpha services api-keys create:*)",
|
||||
"Bash(gcloud components install:*)",
|
||||
"Bash(chmod:*)",
|
||||
"Bash(./fetch-streetview.sh:*)",
|
||||
"Bash(xargs:*)",
|
||||
"Bash(npx @biomejs/biome lint:*)",
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(git push:*)",
|
||||
"Bash(npm run type-check:*)",
|
||||
"Bash(npm run pre-commit:*)",
|
||||
"Bash(git add:*)",
|
||||
"Bash(npm info:*)",
|
||||
"Bash(npx tsc:*)",
|
||||
"Bash(git commit -m \"$(cat <<''EOF''\ndocs: add comprehensive merge conflict resolution guide\n\nAdd detailed guide for intelligent diff3-style merge conflict resolution:\n- Explanation of diff3 format (OURS, BASE, THEIRS)\n- 5 resolution patterns with examples (Compatible, Redundant, Conflicting, Delete vs Modify, Rename + References)\n- zdiff3 modern alternative\n- Semantic merge concepts\n- Best practices and anti-patterns\n- Debugging guide for failed resolutions\n- Quick reference checklist\n\nThis guide helps resolve merge conflicts intelligently by understanding the intent of both sides'' changes.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")",
|
||||
"Bash(git commit -m \"$(cat <<''EOF''\ndocs: add merge conflict resolution section to CLAUDE.md\n\nAdd quick reference section for merge conflict resolution:\n- Link to comprehensive guide (.claude/MERGE_CONFLICT_RESOLUTION.md)\n- Enable zdiff3 command\n- Quick resolution strategy summary\n- Reminder to test thoroughly after resolution\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")",
|
||||
"Bash(git commit -m \"$(cat <<''EOF''\nchore: add auto-approvals for development commands\n\nAdd auto-approvals for common development workflow commands:\n- npm run type-check\n- npm run pre-commit \n- git add\n- npm info\n- npx tsc\n\nThese commands are safe to run automatically during development and code quality checks.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(/tmp/worksheet-preview-new.tsx)",
|
||||
"Bash(npm run format:*)",
|
||||
"Bash(npm run lint:fix:*)",
|
||||
"Bash(npm run lint)",
|
||||
"mcp__sqlite__read_query",
|
||||
"mcp__sqlite__describe_table",
|
||||
"Bash(git push:*)",
|
||||
"Bash(git pull:*)",
|
||||
"Bash(git stash:*)",
|
||||
"Bash(npx @biomejs/biome:*)",
|
||||
"Bash(git rev-parse:*)",
|
||||
"Bash(gh run list:*)",
|
||||
"Bash(npx biome:*)",
|
||||
"WebFetch(domain:www.macintoshrepository.org)",
|
||||
"WebFetch(domain:www.npmjs.com)",
|
||||
"Bash(npm install:*)",
|
||||
"Bash(pnpm add:*)",
|
||||
"Bash(node -e:*)",
|
||||
"Bash(npm search:*)",
|
||||
"Bash(git revert:*)",
|
||||
"Bash(pnpm remove:*)",
|
||||
"Bash(gh run view:*)",
|
||||
"Bash(pnpm install:*)",
|
||||
"Bash(git checkout:*)",
|
||||
"Bash(node server.js:*)",
|
||||
"Bash(git fetch:*)",
|
||||
"Bash(cat:*)",
|
||||
"Bash(npm run test:run:*)",
|
||||
"Bash(for:*)",
|
||||
"Bash(do sleep 30)",
|
||||
"Bash(echo:*)",
|
||||
"Bash(done)",
|
||||
"Bash(do sleep 120)",
|
||||
"Bash(node --version)",
|
||||
"Bash(docker run:*)",
|
||||
"Bash(docker pull:*)",
|
||||
"Bash(docker inspect:*)",
|
||||
"Bash(docker system prune:*)",
|
||||
"Bash(docker stop:*)",
|
||||
"Bash(docker rm:*)",
|
||||
"Bash(docker logs:*)",
|
||||
"Bash(docker exec:*)",
|
||||
"Bash(node --input-type=module -e:*)",
|
||||
"Bash(npm test:*)",
|
||||
"Bash(npx tsx:*)",
|
||||
"Bash(tsc:*)",
|
||||
"Bash(npx vitest:*)",
|
||||
"Bash(ssh:*)",
|
||||
"Bash(break)",
|
||||
"Bash(npm run lint:*)",
|
||||
"WebFetch(domain:strudel.cc)",
|
||||
"WebFetch(domain:club.tidalcycles.org)",
|
||||
"Bash(git reset:*)",
|
||||
"WebFetch(domain:abaci.one)",
|
||||
"Bash(awk:*)",
|
||||
"Bash(sort:*)",
|
||||
"Bash(apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx )",
|
||||
"Bash(apps/web/src/arcade-games/know-your-world/docs/MAPRENDERER_REFACTORING_PLAN.md )",
|
||||
"Bash(apps/web/src/arcade-games/know-your-world/features/magnifier/index.ts )",
|
||||
"Bash(apps/web/src/arcade-games/know-your-world/features/magnifier/useMagnifierStyle.ts )",
|
||||
"Bash(apps/web/src/arcade-games/know-your-world/features/cursor/ )",
|
||||
"Bash(apps/web/src/arcade-games/know-your-world/features/interaction/ )",
|
||||
"Bash(apps/web/src/arcade-games/know-your-world/utils/heatStyles.ts)",
|
||||
"Bash(ping:*)",
|
||||
"WebFetch(domain:typst.app)",
|
||||
"WebFetch(domain:finemotormath.com)",
|
||||
"WebFetch(domain:learnabacusathome.com)",
|
||||
"WebFetch(domain:totton.idirect.com)",
|
||||
"Bash(npx drizzle-kit:*)",
|
||||
"Bash(npm run db:migrate:*)",
|
||||
"mcp__sqlite__list_tables",
|
||||
"Bash(git fetch:*)",
|
||||
"Bash(npx tsc:*)",
|
||||
"Bash(npm run build:*)",
|
||||
"Bash(curl:*)",
|
||||
"Bash(pkill:*)",
|
||||
"Bash(git rev-parse:*)",
|
||||
"Bash(sqlite3:*)",
|
||||
"Bash(npx eslint:*)",
|
||||
"Bash(src/hooks/useDeviceCapabilities.ts )",
|
||||
"Bash(src/arcade-games/know-your-world/hooks/useDeviceCapabilities.ts )",
|
||||
"Bash(src/components/practice/hooks/useDeviceDetection.ts )",
|
||||
"Bash(src/arcade-games/memory-quiz/components/InputPhase.tsx )",
|
||||
"Bash(src/app/api/curriculum/*/sessions/plans/route.ts)",
|
||||
"Bash(src/app/api/curriculum/*/sessions/plans/*/route.ts)",
|
||||
"Bash(src/components/practice/SessionSummary.tsx )",
|
||||
"Bash(src/components/practice/ )",
|
||||
"Bash(src/app/practice/ )",
|
||||
"Bash(src/app/api/curriculum/ )",
|
||||
"Bash(src/hooks/usePlayerCurriculum.ts )",
|
||||
"Bash(src/hooks/useSessionPlan.ts )",
|
||||
"Bash(src/lib/curriculum/ )",
|
||||
"Bash(src/db/schema/player-curriculum.ts )",
|
||||
"Bash(src/db/schema/player-skill-mastery.ts )",
|
||||
"Bash(src/db/schema/practice-sessions.ts )",
|
||||
"Bash(src/db/schema/session-plans.ts )",
|
||||
"Bash(src/db/schema/index.ts )",
|
||||
"Bash(src/types/tutorial.ts )",
|
||||
"Bash(src/utils/problemGenerator.ts )",
|
||||
"Bash(drizzle/ )",
|
||||
"Bash(docs/DAILY_PRACTICE_SYSTEM.md )",
|
||||
"Bash(../../README.md )",
|
||||
"Bash(.claude/CLAUDE.md)",
|
||||
"Bash(mcp__sqlite__describe_table:*)",
|
||||
"Bash(ls:*)",
|
||||
"Bash(mcp__sqlite__list_tables:*)",
|
||||
"Bash(mcp__sqlite__read_query:*)",
|
||||
"Bash(gh api:*)",
|
||||
"Bash(xargs basename:*)",
|
||||
"Bash(apps/web/src/app/practice/[studentId]/configure/ConfigureClient.tsx )",
|
||||
"Bash(apps/web/src/components/practice/ManualSkillSelector.tsx )",
|
||||
"Bash(apps/web/src/components/practice/VerticalProblem.tsx )",
|
||||
"Bash(apps/web/src/components/practice/VerticalProblem.stories.tsx )",
|
||||
"Bash(apps/web/src/types/tutorial.ts )",
|
||||
"Bash(apps/web/src/utils/problemGenerator.ts )",
|
||||
"Bash(apps/web/src/utils/__tests__/cascadingRegrouping.test.ts)"
|
||||
"Bash(gh run view:*)",
|
||||
"Bash(gh run rerun:*)",
|
||||
"Bash(git checkout:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
},
|
||||
"enableAllProjectMcpServers": true,
|
||||
"enabledMcpjsonServers": ["sqlite"]
|
||||
"enabledMcpjsonServers": [
|
||||
"sqlite"
|
||||
]
|
||||
}
|
||||
|
||||
BIN
apps/web/data/sqlite.db.bak
Normal file
BIN
apps/web/data/sqlite.db.bak
Normal file
Binary file not shown.
@@ -116,13 +116,9 @@
|
||||
"abacus_settings_user_id_users_id_fk": {
|
||||
"name": "abacus_settings_user_id_users_id_fk",
|
||||
"tableFrom": "abacus_settings",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsFrom": ["user_id"],
|
||||
"tableTo": "users",
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "no action",
|
||||
"onDelete": "cascade"
|
||||
}
|
||||
@@ -240,9 +236,7 @@
|
||||
"indexes": {
|
||||
"arcade_rooms_code_unique": {
|
||||
"name": "arcade_rooms_code_unique",
|
||||
"columns": [
|
||||
"code"
|
||||
],
|
||||
"columns": ["code"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -339,26 +333,18 @@
|
||||
"arcade_sessions_room_id_arcade_rooms_id_fk": {
|
||||
"name": "arcade_sessions_room_id_arcade_rooms_id_fk",
|
||||
"tableFrom": "arcade_sessions",
|
||||
"columnsFrom": [
|
||||
"room_id"
|
||||
],
|
||||
"columnsFrom": ["room_id"],
|
||||
"tableTo": "arcade_rooms",
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "no action",
|
||||
"onDelete": "cascade"
|
||||
},
|
||||
"arcade_sessions_user_id_users_id_fk": {
|
||||
"name": "arcade_sessions_user_id_users_id_fk",
|
||||
"tableFrom": "arcade_sessions",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsFrom": ["user_id"],
|
||||
"tableTo": "users",
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "no action",
|
||||
"onDelete": "cascade"
|
||||
}
|
||||
@@ -424,9 +410,7 @@
|
||||
"indexes": {
|
||||
"players_user_id_idx": {
|
||||
"name": "players_user_id_idx",
|
||||
"columns": [
|
||||
"user_id"
|
||||
],
|
||||
"columns": ["user_id"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -434,13 +418,9 @@
|
||||
"players_user_id_users_id_fk": {
|
||||
"name": "players_user_id_users_id_fk",
|
||||
"tableFrom": "players",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsFrom": ["user_id"],
|
||||
"tableTo": "users",
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "no action",
|
||||
"onDelete": "cascade"
|
||||
}
|
||||
@@ -514,9 +494,7 @@
|
||||
"indexes": {
|
||||
"idx_room_members_user_id_unique": {
|
||||
"name": "idx_room_members_user_id_unique",
|
||||
"columns": [
|
||||
"user_id"
|
||||
],
|
||||
"columns": ["user_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -524,13 +502,9 @@
|
||||
"room_members_room_id_arcade_rooms_id_fk": {
|
||||
"name": "room_members_room_id_arcade_rooms_id_fk",
|
||||
"tableFrom": "room_members",
|
||||
"columnsFrom": [
|
||||
"room_id"
|
||||
],
|
||||
"columnsFrom": ["room_id"],
|
||||
"tableTo": "arcade_rooms",
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "no action",
|
||||
"onDelete": "cascade"
|
||||
}
|
||||
@@ -605,13 +579,9 @@
|
||||
"room_member_history_room_id_arcade_rooms_id_fk": {
|
||||
"name": "room_member_history_room_id_arcade_rooms_id_fk",
|
||||
"tableFrom": "room_member_history",
|
||||
"columnsFrom": [
|
||||
"room_id"
|
||||
],
|
||||
"columnsFrom": ["room_id"],
|
||||
"tableTo": "arcade_rooms",
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "no action",
|
||||
"onDelete": "cascade"
|
||||
}
|
||||
@@ -713,10 +683,7 @@
|
||||
"indexes": {
|
||||
"idx_room_invitations_user_room": {
|
||||
"name": "idx_room_invitations_user_room",
|
||||
"columns": [
|
||||
"user_id",
|
||||
"room_id"
|
||||
],
|
||||
"columns": ["user_id", "room_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -724,13 +691,9 @@
|
||||
"room_invitations_room_id_arcade_rooms_id_fk": {
|
||||
"name": "room_invitations_room_id_arcade_rooms_id_fk",
|
||||
"tableFrom": "room_invitations",
|
||||
"columnsFrom": [
|
||||
"room_id"
|
||||
],
|
||||
"columnsFrom": ["room_id"],
|
||||
"tableTo": "arcade_rooms",
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "no action",
|
||||
"onDelete": "cascade"
|
||||
}
|
||||
@@ -833,13 +796,9 @@
|
||||
"room_reports_room_id_arcade_rooms_id_fk": {
|
||||
"name": "room_reports_room_id_arcade_rooms_id_fk",
|
||||
"tableFrom": "room_reports",
|
||||
"columnsFrom": [
|
||||
"room_id"
|
||||
],
|
||||
"columnsFrom": ["room_id"],
|
||||
"tableTo": "arcade_rooms",
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "no action",
|
||||
"onDelete": "cascade"
|
||||
}
|
||||
@@ -918,10 +877,7 @@
|
||||
"indexes": {
|
||||
"idx_room_bans_user_room": {
|
||||
"name": "idx_room_bans_user_room",
|
||||
"columns": [
|
||||
"user_id",
|
||||
"room_id"
|
||||
],
|
||||
"columns": ["user_id", "room_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -929,13 +885,9 @@
|
||||
"room_bans_room_id_arcade_rooms_id_fk": {
|
||||
"name": "room_bans_room_id_arcade_rooms_id_fk",
|
||||
"tableFrom": "room_bans",
|
||||
"columnsFrom": [
|
||||
"room_id"
|
||||
],
|
||||
"columnsFrom": ["room_id"],
|
||||
"tableTo": "arcade_rooms",
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "no action",
|
||||
"onDelete": "cascade"
|
||||
}
|
||||
@@ -998,13 +950,9 @@
|
||||
"user_stats_user_id_users_id_fk": {
|
||||
"name": "user_stats_user_id_users_id_fk",
|
||||
"tableFrom": "user_stats",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsFrom": ["user_id"],
|
||||
"tableTo": "users",
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "no action",
|
||||
"onDelete": "cascade"
|
||||
}
|
||||
@@ -1062,16 +1010,12 @@
|
||||
"indexes": {
|
||||
"users_guest_id_unique": {
|
||||
"name": "users_guest_id_unique",
|
||||
"columns": [
|
||||
"guest_id"
|
||||
],
|
||||
"columns": ["guest_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"users_email_unique": {
|
||||
"name": "users_email_unique",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -1091,4 +1035,4 @@
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,4 +234,4 @@
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ const PURPOSE_WEIGHTS = {
|
||||
focus: DEFAULT_PLAN_CONFIG.focusWeight,
|
||||
reinforce: DEFAULT_PLAN_CONFIG.reinforceWeight,
|
||||
review: DEFAULT_PLAN_CONFIG.reviewWeight,
|
||||
challenge: DEFAULT_PLAN_CONFIG.challengeWeight,
|
||||
// Note: Challenge slots are calculated as remainder after focus/reinforce/review
|
||||
// and use CHALLENGE_RATIO_BY_PART_TYPE in the actual session planner
|
||||
}
|
||||
|
||||
interface ConfigureClientProps {
|
||||
|
||||
@@ -2,6 +2,15 @@ import { createId } from '@paralleldrive/cuid2'
|
||||
import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
|
||||
import type { SkillSet } from '@/types/tutorial'
|
||||
import { players } from './players'
|
||||
import {
|
||||
DEFAULT_SECONDS_PER_PROBLEM,
|
||||
PART_TIME_WEIGHTS,
|
||||
PURPOSE_COMPLEXITY_BOUNDS,
|
||||
PURPOSE_WEIGHTS,
|
||||
REVIEW_INTERVAL_DAYS,
|
||||
SESSION_TIMEOUT_HOURS,
|
||||
TERM_COUNT_RANGES,
|
||||
} from '@/lib/curriculum/config'
|
||||
|
||||
// ============================================================================
|
||||
// Types for JSON fields
|
||||
@@ -451,98 +460,33 @@ export function calculateSessionHealth(plan: SessionPlan, elapsedTimeMs: number)
|
||||
}
|
||||
|
||||
/**
|
||||
* Default configuration for plan generation
|
||||
* Default configuration for plan generation.
|
||||
*
|
||||
* All values are imported from @/lib/curriculum/config for centralized tuning.
|
||||
* Edit those config files to change these defaults.
|
||||
*/
|
||||
export const DEFAULT_PLAN_CONFIG = {
|
||||
/** Distribution weights for slot purposes (should sum to 1.0) */
|
||||
focusWeight: 0.6,
|
||||
reinforceWeight: 0.2,
|
||||
reviewWeight: 0.15,
|
||||
challengeWeight: 0.05,
|
||||
// Slot purpose distribution (from config/slot-distribution.ts)
|
||||
// Note: Challenge slots use CHALLENGE_RATIO_BY_PART_TYPE instead of a fixed weight
|
||||
focusWeight: PURPOSE_WEIGHTS.focus,
|
||||
reinforceWeight: PURPOSE_WEIGHTS.reinforce,
|
||||
reviewWeight: PURPOSE_WEIGHTS.review,
|
||||
|
||||
/** Distribution weights for session parts (should sum to 1.0)
|
||||
* Part 1 (abacus): 50% - This is where new skills are built
|
||||
* Part 2 (visualization): 30% - Mental math with visualization
|
||||
* Part 3 (linear): 20% - Mental math in sentence format
|
||||
*/
|
||||
partTimeWeights: {
|
||||
abacus: 0.5,
|
||||
visualization: 0.3,
|
||||
linear: 0.2,
|
||||
},
|
||||
// Session part time distribution (from config/slot-distribution.ts)
|
||||
partTimeWeights: PART_TIME_WEIGHTS,
|
||||
|
||||
/** Term count range for abacus part (how many numbers per problem) */
|
||||
abacusTermCount: { min: 3, max: 6 },
|
||||
// Term count ranges (from config/slot-distribution.ts)
|
||||
abacusTermCount: TERM_COUNT_RANGES.abacus,
|
||||
visualizationTermCount: TERM_COUNT_RANGES.visualization,
|
||||
linearTermCount: TERM_COUNT_RANGES.linear,
|
||||
|
||||
/** Term count range for visualization part (defaults to 75% of abacus) */
|
||||
visualizationTermCount: null as { min: number; max: number } | null,
|
||||
// Timing (from config/session-timing.ts)
|
||||
defaultSecondsPerProblem: DEFAULT_SECONDS_PER_PROBLEM,
|
||||
reviewIntervalDays: REVIEW_INTERVAL_DAYS,
|
||||
sessionTimeoutHours: SESSION_TIMEOUT_HOURS,
|
||||
|
||||
/** Term count range for linear part (same as abacus by default) */
|
||||
linearTermCount: null as { min: number; max: number } | null,
|
||||
|
||||
/** Default seconds per problem if no history */
|
||||
defaultSecondsPerProblem: 45,
|
||||
|
||||
/** Spaced repetition intervals */
|
||||
reviewIntervalDays: {
|
||||
mastered: 7,
|
||||
practicing: 3,
|
||||
},
|
||||
|
||||
/** Session timeout in hours - sessions older than this are auto-abandoned on next access */
|
||||
sessionTimeoutHours: 24,
|
||||
|
||||
/**
|
||||
* Complexity budget per term for each part type.
|
||||
* null = no limit (unlimited)
|
||||
*
|
||||
* These are evaluated against student-aware costs:
|
||||
* termCost = Σ(baseCost × masteryMultiplier)
|
||||
*
|
||||
* @deprecated Use purposeComplexityBounds instead for per-purpose control
|
||||
*/
|
||||
abacusComplexityBudget: null as number | null,
|
||||
visualizationComplexityBudget: 3 as number | null,
|
||||
linearComplexityBudget: null as number | null,
|
||||
|
||||
/**
|
||||
* Complexity bounds per purpose type, per part type.
|
||||
*
|
||||
* Each purpose can have different min/max complexity requirements.
|
||||
* null = no constraint (falls back to part-type defaults above)
|
||||
*
|
||||
* Evaluated against student-aware costs:
|
||||
* termCost = Σ(baseCost × masteryMultiplier)
|
||||
*
|
||||
* Example bounds:
|
||||
* min: 1 = every term must use at least one five-complement
|
||||
* max: 3 = no term can exceed cost 3 (cap for visualization)
|
||||
*/
|
||||
purposeComplexityBounds: {
|
||||
focus: {
|
||||
abacus: { min: null, max: null },
|
||||
visualization: { min: null, max: 3 },
|
||||
linear: { min: null, max: null },
|
||||
},
|
||||
reinforce: {
|
||||
abacus: { min: null, max: null },
|
||||
visualization: { min: null, max: 3 },
|
||||
linear: { min: null, max: null },
|
||||
},
|
||||
review: {
|
||||
abacus: { min: null, max: null },
|
||||
visualization: { min: null, max: 3 },
|
||||
linear: { min: null, max: null },
|
||||
},
|
||||
challenge: {
|
||||
abacus: { min: 1, max: null },
|
||||
visualization: { min: 1, max: null },
|
||||
linear: { min: 1, max: null },
|
||||
},
|
||||
} as Record<
|
||||
'focus' | 'reinforce' | 'review' | 'challenge',
|
||||
Record<'abacus' | 'visualization' | 'linear', { min: number | null; max: number | null }>
|
||||
>,
|
||||
// Per-purpose complexity bounds (from config/complexity-budgets.ts)
|
||||
purposeComplexityBounds: PURPOSE_COMPLEXITY_BOUNDS,
|
||||
}
|
||||
|
||||
export type PlanGenerationConfig = typeof DEFAULT_PLAN_CONFIG
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
* fluency in a skill and how fluency decays over time.
|
||||
*/
|
||||
|
||||
console.log('[fluency-thresholds.ts] MODULE LOADING...')
|
||||
|
||||
// =============================================================================
|
||||
// Fluency Achievement Thresholds
|
||||
// =============================================================================
|
||||
@@ -80,21 +82,23 @@ export const REINFORCEMENT_CONFIG = {
|
||||
|
||||
/**
|
||||
* Mastery credit multipliers based on help level.
|
||||
* Full credit only given for unassisted correct answers.
|
||||
* Used when updating skill mastery after a correct answer.
|
||||
*
|
||||
* - 0 (no help): 1.0 = full credit
|
||||
* - 1 (hint): 0.8 = 80% credit
|
||||
* - 2 (decomposition): 0.5 = 50% credit
|
||||
* - 3 (bead arrows): 0.3 = 30% credit
|
||||
* - 1 (hint): 1.0 = full credit (hints don't reduce credit)
|
||||
* - 2 (decomposition): 0.5 = half credit
|
||||
* - 3 (bead arrows): 0.25 = quarter credit
|
||||
*/
|
||||
masteryCreditByHelpLevel: {
|
||||
creditMultipliers: {
|
||||
0: 1.0,
|
||||
1: 0.8,
|
||||
1: 1.0,
|
||||
2: 0.5,
|
||||
3: 0.3,
|
||||
} as Record<number, number>,
|
||||
3: 0.25,
|
||||
} as Record<0 | 1 | 2 | 3, number>,
|
||||
} as const
|
||||
|
||||
export type FluencyThresholds = typeof FLUENCY_THRESHOLDS
|
||||
export type FluencyRecency = typeof FLUENCY_RECENCY
|
||||
export type ReinforcementConfig = typeof REINFORCEMENT_CONFIG
|
||||
|
||||
console.log('[fluency-thresholds.ts] MODULE LOADED - REINFORCEMENT_CONFIG:', JSON.stringify(REINFORCEMENT_CONFIG, null, 2))
|
||||
|
||||
@@ -29,8 +29,8 @@ export const PURPOSE_WEIGHTS = {
|
||||
reinforce: 0.2,
|
||||
/** Spaced repetition of mastered skills */
|
||||
review: 0.15,
|
||||
/** Legacy weight - challenge allocation now uses challengeRatioByPartType */
|
||||
challenge: 0.05,
|
||||
// Note: Challenge slots are allocated using CHALLENGE_RATIO_BY_PART_TYPE (per part type),
|
||||
// not a fixed weight. See session-planner.ts for the allocation logic.
|
||||
} as const
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,14 @@ import {
|
||||
// Import directly from source to avoid circular dependency issues with re-exports
|
||||
import { REINFORCEMENT_CONFIG } from '@/lib/curriculum/config/fluency-thresholds'
|
||||
import type { NewPracticeSession, PracticeSession } from '@/db/schema/practice-sessions'
|
||||
|
||||
// Debug: Log the imported config at module load time
|
||||
console.log('[progress-manager.ts] MODULE LOAD - REINFORCEMENT_CONFIG debug:', {
|
||||
exists: typeof REINFORCEMENT_CONFIG !== 'undefined',
|
||||
type: typeof REINFORCEMENT_CONFIG,
|
||||
keys: REINFORCEMENT_CONFIG ? Object.keys(REINFORCEMENT_CONFIG) : 'N/A',
|
||||
fullObject: JSON.stringify(REINFORCEMENT_CONFIG, null, 2),
|
||||
})
|
||||
import type { HelpLevel } from '@/db/schema/session-plans'
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -549,9 +549,7 @@ export async function recordSlotResult(
|
||||
|
||||
// Defensive check: ensure results array exists
|
||||
if (!plan.results || !Array.isArray(plan.results)) {
|
||||
throw new Error(
|
||||
`Plan ${planId} has invalid results: ${typeof plan.results} (expected array)`
|
||||
)
|
||||
throw new Error(`Plan ${planId} has invalid results: ${typeof plan.results} (expected array)`)
|
||||
}
|
||||
|
||||
console.log(`[recordSlotResult] Creating newResult for plan ${planId}`)
|
||||
@@ -593,7 +591,9 @@ export async function recordSlotResult(
|
||||
let updatedHealth
|
||||
try {
|
||||
updatedHealth = calculateSessionHealth({ ...plan, results: updatedResults }, elapsedMs)
|
||||
console.log(`[recordSlotResult] calculateSessionHealth succeeded: ${JSON.stringify(updatedHealth)}`)
|
||||
console.log(
|
||||
`[recordSlotResult] calculateSessionHealth succeeded: ${JSON.stringify(updatedHealth)}`
|
||||
)
|
||||
} catch (healthError) {
|
||||
console.error(`[recordSlotResult] calculateSessionHealth FAILED:`, healthError)
|
||||
throw healthError
|
||||
|
||||
Reference in New Issue
Block a user