- 통계 탭 삭제 (프론트 페이지/차트/네비 항목, 백엔드 stats 모듈은 대시보드용으로 유지) - PDF 문제 분리 → 문제집 생성으로 변경 (ProblemSet + Problem 레코드) - ProblemSet에 sourceType, uploadedByUserId 추가 - POST /problem-sets/create-from-pdf 엔드포인트 - GET /problem-sets/my-uploads, DELETE /problem-sets/:id - exams 페이지에 '내가 만든 문제집' 섹션 - 문제 풀이 UI 통일 - DrawingCanvas 공통 컴포넌트 추출 (필기모드) - 복습 페이지에 필기모드(캔버스+펜색상) 추가 - 다시풀기 페이지에 필기모드+메모 추가 - 처음풀기 페이지 난이도 슬라이더→3버튼(H/M/E)+SM-2 연동 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
464 lines
22 KiB
JSON
464 lines
22 KiB
JSON
{
|
||
"version": "1.0.0",
|
||
"legacy_version": "1.0.0",
|
||
"artifactType": "precompact-snapshot",
|
||
"timestamp": "2026-04-17T00:55:24.687Z",
|
||
"sessionId": "",
|
||
"previous_state": {
|
||
"summary": "Before compaction: 20 recent edit(s)",
|
||
"session_state": null,
|
||
"plan_counts": {
|
||
"total": 24,
|
||
"wip": 0,
|
||
"blocked": 0,
|
||
"recent_edits": 20
|
||
}
|
||
},
|
||
"next_action": {
|
||
"taskId": "7B.1",
|
||
"task": "Prisma: `UserProblemSet` 모델 (`userId`, `problemSetId`, `addedAt`, `archivedAt DateTime?`, `@@unique([userId, problemSetId])`, `@@index([userId])`)",
|
||
"dod": "migration 통과, User/ProblemSet 양방향 relation 정의됨",
|
||
"depends": "7A.1",
|
||
"status": "cc:TODO",
|
||
"source": "Plans.md",
|
||
"priority": "normal",
|
||
"summary": "Continue 7B.1 Prisma: `UserProblemSet` 모델 (`userId`, `problemSetId`, `addedAt`, `archivedAt DateTime?`, `@@unique([userId, problemSetId])`, `@@index([userId])`)"
|
||
},
|
||
"open_risks": [
|
||
{
|
||
"severity": "medium",
|
||
"kind": "verification",
|
||
"summary": "20 recent edit(s) should be re-validated after resume",
|
||
"detail": ".claude/sessions/.last_inbox_check, .claude/state/plans-state.json, .claude/state/tool-failure-counter.txt, .claude/logs/subagent-history.jsonl, .claude/state/breezing-timeline.jsonl"
|
||
}
|
||
],
|
||
"failed_checks": [],
|
||
"decision_log": [
|
||
{
|
||
"timestamp": "2026-04-17T00:55:24.687Z",
|
||
"actor": "pre-compact-save",
|
||
"decision": "canonical_handoff_artifact_written",
|
||
"rationale": "Persist a stable JSON artifact in .claude/state for long-running session handoff."
|
||
},
|
||
{
|
||
"timestamp": "2026-04-17T00:55:24.687Z",
|
||
"actor": "pre-compact-save",
|
||
"decision": "legacy_snapshot_mirrored",
|
||
"rationale": "Keep precompact-snapshot.json for backward compatibility with older hooks."
|
||
},
|
||
{
|
||
"timestamp": "2026-04-17T00:55:24.687Z",
|
||
"actor": "pre-compact-save",
|
||
"decision": "next_action_selected",
|
||
"rationale": "Continue 7B.1 Prisma: `UserProblemSet` 모델 (`userId`, `problemSetId`, `addedAt`, `archivedAt DateTime?`, `@@unique([userId, problemSetId])`, `@@index([userId])`) (source: Plans.md)"
|
||
}
|
||
],
|
||
"context_reset": {
|
||
"policy": {
|
||
"mode": "auto",
|
||
"dryRun": false,
|
||
"thresholds": {
|
||
"wipTasks": 4,
|
||
"blockedTasks": 1,
|
||
"recentEdits": 8,
|
||
"failedChecks": 1,
|
||
"sessionAgeMinutes": 120
|
||
}
|
||
},
|
||
"recommended": true,
|
||
"summary": "Context reset recommended (auto): 20 recent edit count exceed threshold 8",
|
||
"reasons": [
|
||
"20 recent edit count exceed threshold 8"
|
||
],
|
||
"candidates": [
|
||
{
|
||
"key": "wip_tasks",
|
||
"label": "WIP task count",
|
||
"actual": 0,
|
||
"threshold": 4,
|
||
"triggered": false
|
||
},
|
||
{
|
||
"key": "blocked_tasks",
|
||
"label": "blocked task count",
|
||
"actual": 0,
|
||
"threshold": 1,
|
||
"triggered": false
|
||
},
|
||
{
|
||
"key": "recent_edits",
|
||
"label": "recent edit count",
|
||
"actual": 20,
|
||
"threshold": 8,
|
||
"triggered": true
|
||
},
|
||
{
|
||
"key": "failed_checks",
|
||
"label": "failed check count",
|
||
"actual": 0,
|
||
"threshold": 1,
|
||
"triggered": false
|
||
},
|
||
{
|
||
"key": "session_age_minutes",
|
||
"label": "session age (minutes)",
|
||
"actual": 0,
|
||
"threshold": 120,
|
||
"triggered": false
|
||
}
|
||
],
|
||
"counters": {
|
||
"wipTasks": 0,
|
||
"blockedTasks": 0,
|
||
"recentEdits": 20,
|
||
"failedChecks": 0,
|
||
"sessionAgeMinutes": null
|
||
}
|
||
},
|
||
"continuity": {
|
||
"plugin_first_workflow": true,
|
||
"resume_aware_effort_continuity": true,
|
||
"effort_hint": "medium",
|
||
"active_skill": null,
|
||
"summary": "plugin-first workflow: enabled; resume-aware effort continuity: medium; next_task=7B.1"
|
||
},
|
||
"planItems": [
|
||
{
|
||
"taskId": "7B.1",
|
||
"title": "Prisma: `UserProblemSet` 모델 (`userId`, `problemSetId`, `addedAt`, `archivedAt DateTime?`, `@@unique([userId, problemSetId])`, `@@index([userId])`)",
|
||
"dod": "migration 통과, User/ProblemSet 양방향 relation 정의됨",
|
||
"depends": "7A.1",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "7B.2",
|
||
"title": "backend `me/problem-sets` module: `GET /api/me/problem-sets` (내 추가분 list), `POST /api/me/problem-sets` (`{ problemSetId }`), `DELETE /api/me/problem-sets/:id` (soft archive)",
|
||
"dod": "데모 계정으로 3개 API 모두 200/201/204 응답. 중복 추가 시 409",
|
||
"depends": "7B.1",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "7B.3",
|
||
"title": "frontend `lib/api.ts` 에 `UserProblemSet`, `addProblemSet`, `listMyProblemSets`, `removeProblemSet` 타입/헬퍼 추가",
|
||
"dod": "타입 에러 없이 컴파일, stats 페이지에서 호출 가능",
|
||
"depends": "7B.2",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "7B.4",
|
||
"title": "frontend `/study` 페이지 탭 재설계: [내 문제집 / 카탈로그 / 직접 입력] 3 탭. \"카탈로그\" 에 전체 ProblemSet 목록 + 각 카드에 \"+ 내 목록에 추가\" 버튼. \"내 문제집\" 에 추가한 것만 표시",
|
||
"dod": "카탈로그에서 카드 클릭 → 내 목록 탭에 즉시 반영. 중복 클릭 방지",
|
||
"depends": "7B.3",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "7B.5",
|
||
"title": "\"내 문제집\" 탭 상세: 선택한 ProblemSet → 문항 리스트 → 각 문항 \"풀기\" 버튼 → 기존 `/study/form?problemId=N` 으로 이동해 결과 입력",
|
||
"dod": "데모 계정에서 2026 수학 → 15번 클릭 → 결과 기록 → 복습 큐 진입까지 전 플로우 통과",
|
||
"depends": "7B.4",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10A.1",
|
||
"title": "Prisma 스키마: `MathUnit` enum, `ProblemSet.mathUnit MathUnit?`, `User.bojHandle String? @db.VarChar(32)`, `User.focusUnits Json?`. migration `phase10_math_units_and_ps` 생성",
|
||
"dod": "`prisma migrate dev --name phase10_math_units_and_ps` 성공, `@prisma/client` 재생성, 기존 행 NULL 기본값",
|
||
"depends": "-",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10A.2",
|
||
"title": "비수학 데이터 wipe 스크립트 `backend/scripts/wipe-non-math.ts` — `ProblemSet where subjectName != '수학'` 연쇄 삭제(Assignment/AssignmentSubmission, StudyLog, ReviewSchedule, Problem, Passage 포함). 실행 전 mysqldump 백업을 `backups/` 에 저장",
|
||
"dod": "dry-run 시 삭제 대상 수 출력, `--apply` 시 실제 삭제 후 `ProblemSet count where subjectName='수학'` 만 남음",
|
||
"depends": "10A.1",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10A.3",
|
||
"title": "`kice-import.ts` 리팩터: `TARGET_SUBJECTS = ['수학']`, 30문항을 1~22 = common, 23~30 = 선택 × 3 단원으로 split 하여 4 ProblemSet 생성. `ProblemSet.title` 은 `YYYY학년도 수능 수학 (공통)` 형태",
|
||
"dod": "`pnpm cli:kice-import --dry-run --year=2026` 시 common 22 + 확통 8 + 미적 8 + 기하 8 = 46 문항 리포트",
|
||
"depends": "10A.2",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10A.4",
|
||
"title": "`backend/src/problem-sets/parsing/strategies/kice.ts` 및 관련 OCR 프롬프트에서 비수학 분기 제거. `shared.ts`·`ocr-fix.ts`·`seed.ts` 에서 비수학 과목 참조 삭제",
|
||
"dod": "`grep -rE \"국어|영어|한국사|생활과 윤리|사회탐구\" backend/src backend/scripts` 결과 0",
|
||
"depends": "10A.3",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10A.5",
|
||
"title": "`data/kice/{2025,2026}/{국어,영어,한국사,사회탐구}/` 삭제 + `.gitignore` 정리. `backend/uploads/problems/{year}/{비수학 과목}/` 도 삭제",
|
||
"dod": "디스크에 수학 PDF + 수학 이미지 crop 만 남음",
|
||
"depends": "10A.4",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10B.1",
|
||
"title": "Prisma: `model PsProblem { id Int, bojId Int @unique, title, level Int, tags Json, acceptedUserCount Int?, averageTries Float?, updatedAt }`, `model PsBookmark { userId, psProblemId, createdAt @@unique([userId,psProblemId]) }`, `StudyLog.psProblemId Int?` FK 추가. migration `phase10_ps_tables`",
|
||
"dod": "`prisma migrate dev` 성공, 관계 매핑 검증",
|
||
"depends": "10A.1",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10B.2",
|
||
"title": "`backend/src/ps/solved-ac.client.ts` — axios 인스턴스(`https://solved.ac/api/v3`, User-Agent 명시, 타임아웃 10s, 지수 백오프 3회). 메서드: `searchProblems(query, page)`, `getProblem(bojId)`, `getUser(handle)`, `getUserSolvedProblems(handle, page)`. 내부 rate-limit (초당 3req)",
|
||
"dod": "단위 테스트: mocked axios 로 4 메서드 응답 파싱 정상",
|
||
"depends": "10B.1",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10B.3",
|
||
"title": "`PsModule` + `PsService` + `PsController` — endpoints: `GET /api/ps/search?q=&level=&page=`, `GET /api/ps/problems/:bojId`, `POST /api/ps/bookmarks { bojId }`, `DELETE /api/ps/bookmarks/:bojId`, `GET /api/ps/bookmarks`, `POST /api/ps/sync { bojHandle? }` (핸들 없으면 `User.bojHandle` 사용). JWT guard + throttler",
|
||
"dod": "Postman/curl 6 endpoint 200, sync 실행 시 PsProblem upsert + 시스템 Subject \"PS\" 생성 + StudyLog bulk insert, 멱등성 확인(중복 sync 시 같은 BOJ 문제 StudyLog 추가 생성 안 함)",
|
||
"depends": "10B.2",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10B.4",
|
||
"title": "Sync 멱등성 저장소: `PsSyncState { userId @unique, lastSyncedAt, lastSolvedCount }` 추가해 마지막 sync 이후 새로 맞힌 문제만 가져옴. 초기 sync 는 `top_100` 기준 상위 100개로 제한(과도한 StudyLog 폭주 방지)",
|
||
"dod": "1차 sync 후 `PsSyncState.lastSolvedCount` 저장, 2차 sync 는 차분만 insert",
|
||
"depends": "10B.3",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10C.1",
|
||
"title": "`frontend/src/app/onboarding/page.tsx` — GoalType 옵션 라벨/설명 교체. `math-suneung` 선택 시 4 단원 체크박스 그룹 노출, `ps` 선택 시 `bojHandle` 인풋(정규식 검증 `^[a-zA-Z0-9_-]{3,20}$`) 노출",
|
||
"dod": "각 GoalType 분기 UI 정상, 저장 후 `/auth/me` 응답에 `focusUnits` 또는 `bojHandle` 반영",
|
||
"depends": "10B.1",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10C.2",
|
||
"title": "`frontend/src/lib/api.ts` + `MeUser` 타입: `bojHandle`, `focusUnits: MathUnit[]`, `GoalType` 유니언 갱신. `api/me` PATCH payload 확장",
|
||
"dod": "타입 에러 0, API PATCH 200",
|
||
"depends": "10C.1",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10C.3",
|
||
"title": "전역 과목 리스트 정리 — `subjects/page.tsx`, `exams/page.tsx`, `review/page.tsx`, `review/history/page.tsx`, `dashboard`, `school/*` 에서 비수학 하드코딩 참조 제거. `exam/shared.ts::getExamDurationSeconds` → 수학 100분 고정. `SideNav` 에서 국어/영어/한국사 링크 제거",
|
||
"dod": "`grep -rE \"국어|영어|한국사|생활과 윤리\" frontend/src` = 0",
|
||
"depends": "10C.2",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10C.4",
|
||
"title": "`frontend/src/app/ps/` 신규 섹션 — `page.tsx`(검색 + 필터 + 북마크 버튼), `bookmarks/page.tsx`(북마크 목록), `sync/page.tsx`(핸들 동기화 버튼 + 상태). BottomNav/SideNav 에 `PS` 진입점 추가",
|
||
"dod": "4 경로 200 + search/bookmark/sync e2e 수동 검증 통과",
|
||
"depends": "10C.2",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10C.5",
|
||
"title": "`landing`/`pricing`/`dashboard` 카피 업데이트 — \"수능 국어/영어…\" 문구를 \"수학 수능 + PS\" 로 교체, 아카이브 design HTML 과 docs 에서도 혼동 없도록 정리",
|
||
"dod": "`grep -rE \"국어|영어|한국사|생활과 윤리\" frontend/src docs README.md` = 0 (아카이브 `.claude/archive/` 는 제외)",
|
||
"depends": "10C.3",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10D.1",
|
||
"title": "로컬 커밋 + push → Gitea `hanarang/reloop-v2 master`. commit 단위는 10A / 10B / 10C 별로 나눔",
|
||
"dod": "`git push` 성공, `tea pr list` 없이 master 직접 반영",
|
||
"depends": "10C.5",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10D.2",
|
||
"title": "`ssh dev@10.10.10.169` → `cd ~/reloop-v2 && git pull`. backend 에서 `mysqldump reloop_v2 > backups/phase10-pre.sql` 실행 후 `pnpm --filter backend exec prisma migrate deploy` → `pnpm --filter backend build`",
|
||
"dod": "마이그레이션 적용됨, dist/main.js 빌드 성공, DB 백업 존재",
|
||
"depends": "10D.1",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10D.3",
|
||
"title": "비수학 wipe 스크립트 운영 실행 — `node backend/dist/scripts/wipe-non-math.js --apply`. 이어서 `pnpm cli:kice-import --year=2025 && --year=2026` 로 수학 8 ProblemSet 재생성",
|
||
"dod": "DB 에 수학 8 ProblemSet / 92 problems, 비수학 0",
|
||
"depends": "10D.2",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10D.4",
|
||
"title": "Frontend 빌드 + standalone copy + `pm2 reload reloop-web reloop-api --update-env`",
|
||
"dod": "PM2 status online, `curl https://reloop-api.nabomhalang.co.kr/health` 200, `curl https://reloop.nabomhalang.co.kr` 200",
|
||
"depends": "10D.3",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
},
|
||
{
|
||
"taskId": "10D.5",
|
||
"title": "스모크 테스트 — 온보딩 `math-suneung` 플로우 + 단원 선택 저장, `ps` 플로우 + 더미 BOJ 핸들 sync, `/ps/search` 검색, `/exams` 에 수학 4단원 표시, `/review` 에 비수학 흔적 없음",
|
||
"dod": "5 시나리오 수동 통과 스크린샷 또는 로그 캡처",
|
||
"depends": "10D.4",
|
||
"status": "cc:TODO",
|
||
"tags": {
|
||
"todo": true,
|
||
"wip": false,
|
||
"blocked": false
|
||
}
|
||
}
|
||
],
|
||
"wipTasks": [
|
||
"Prisma: `UserProblemSet` 모델 (`userId`, `problemSetId`, `addedAt`, `archivedAt DateTime?`, `@@unique([userId, problemSetId])`, `@@index([userId])`)",
|
||
"backend `me/problem-sets` module: `GET /api/me/problem-sets` (내 추가분 list), `POST /api/me/problem-sets` (`{ problemSetId }`), `DELETE /api/me/problem-sets/:id` (soft archive)",
|
||
"frontend `lib/api.ts` 에 `UserProblemSet`, `addProblemSet`, `listMyProblemSets`, `removeProblemSet` 타입/헬퍼 추가",
|
||
"frontend `/study` 페이지 탭 재설계: [내 문제집 / 카탈로그 / 직접 입력] 3 탭. \"카탈로그\" 에 전체 ProblemSet 목록 + 각 카드에 \"+ 내 목록에 추가\" 버튼. \"내 문제집\" 에 추가한 것만 표시",
|
||
"\"내 문제집\" 탭 상세: 선택한 ProblemSet → 문항 리스트 → 각 문항 \"풀기\" 버튼 → 기존 `/study/form?problemId=N` 으로 이동해 결과 입력",
|
||
"Prisma 스키마: `MathUnit` enum, `ProblemSet.mathUnit MathUnit?`, `User.bojHandle String? @db.VarChar(32)`, `User.focusUnits Json?`. migration `phase10_math_units_and_ps` 생성",
|
||
"비수학 데이터 wipe 스크립트 `backend/scripts/wipe-non-math.ts` — `ProblemSet where subjectName != '수학'` 연쇄 삭제(Assignment/AssignmentSubmission, StudyLog, ReviewSchedule, Problem, Passage 포함). 실행 전 mysqldump 백업을 `backups/` 에 저장",
|
||
"`kice-import.ts` 리팩터: `TARGET_SUBJECTS = ['수학']`, 30문항을 1~22 = common, 23~30 = 선택 × 3 단원으로 split 하여 4 ProblemSet 생성. `ProblemSet.title` 은 `YYYY학년도 수능 수학 (공통)` 형태",
|
||
"`backend/src/problem-sets/parsing/strategies/kice.ts` 및 관련 OCR 프롬프트에서 비수학 분기 제거. `shared.ts`·`ocr-fix.ts`·`seed.ts` 에서 비수학 과목 참조 삭제",
|
||
"`data/kice/{2025,2026}/{국어,영어,한국사,사회탐구}/` 삭제 + `.gitignore` 정리. `backend/uploads/problems/{year}/{비수학 과목}/` 도 삭제",
|
||
"Prisma: `model PsProblem { id Int, bojId Int @unique, title, level Int, tags Json, acceptedUserCount Int?, averageTries Float?, updatedAt }`, `model PsBookmark { userId, psProblemId, createdAt @@unique([userId,psProblemId]) }`, `StudyLog.psProblemId Int?` FK 추가. migration `phase10_ps_tables`",
|
||
"`backend/src/ps/solved-ac.client.ts` — axios 인스턴스(`https://solved.ac/api/v3`, User-Agent 명시, 타임아웃 10s, 지수 백오프 3회). 메서드: `searchProblems(query, page)`, `getProblem(bojId)`, `getUser(handle)`, `getUserSolvedProblems(handle, page)`. 내부 rate-limit (초당 3req)",
|
||
"`PsModule` + `PsService` + `PsController` — endpoints: `GET /api/ps/search?q=&level=&page=`, `GET /api/ps/problems/:bojId`, `POST /api/ps/bookmarks { bojId }`, `DELETE /api/ps/bookmarks/:bojId`, `GET /api/ps/bookmarks`, `POST /api/ps/sync { bojHandle? }` (핸들 없으면 `User.bojHandle` 사용). JWT guard + throttler",
|
||
"Sync 멱등성 저장소: `PsSyncState { userId @unique, lastSyncedAt, lastSolvedCount }` 추가해 마지막 sync 이후 새로 맞힌 문제만 가져옴. 초기 sync 는 `top_100` 기준 상위 100개로 제한(과도한 StudyLog 폭주 방지)",
|
||
"`frontend/src/app/onboarding/page.tsx` — GoalType 옵션 라벨/설명 교체. `math-suneung` 선택 시 4 단원 체크박스 그룹 노출, `ps` 선택 시 `bojHandle` 인풋(정규식 검증 `^[a-zA-Z0-9_-]{3,20}$`) 노출",
|
||
"`frontend/src/lib/api.ts` + `MeUser` 타입: `bojHandle`, `focusUnits: MathUnit[]`, `GoalType` 유니언 갱신. `api/me` PATCH payload 확장",
|
||
"전역 과목 리스트 정리 — `subjects/page.tsx`, `exams/page.tsx`, `review/page.tsx`, `review/history/page.tsx`, `dashboard`, `school/*` 에서 비수학 하드코딩 참조 제거. `exam/shared.ts::getExamDurationSeconds` → 수학 100분 고정. `SideNav` 에서 국어/영어/한국사 링크 제거",
|
||
"`frontend/src/app/ps/` 신규 섹션 — `page.tsx`(검색 + 필터 + 북마크 버튼), `bookmarks/page.tsx`(북마크 목록), `sync/page.tsx`(핸들 동기화 버튼 + 상태). BottomNav/SideNav 에 `PS` 진입점 추가",
|
||
"`landing`/`pricing`/`dashboard` 카피 업데이트 — \"수능 국어/영어…\" 문구를 \"수학 수능 + PS\" 로 교체, 아카이브 design HTML 과 docs 에서도 혼동 없도록 정리",
|
||
"로컬 커밋 + push → Gitea `hanarang/reloop-v2 master`. commit 단위는 10A / 10B / 10C 별로 나눔",
|
||
"`ssh dev@10.10.10.169` → `cd ~/reloop-v2 && git pull`. backend 에서 `mysqldump reloop_v2 > backups/phase10-pre.sql` 실행 후 `pnpm --filter backend exec prisma migrate deploy` → `pnpm --filter backend build`",
|
||
"비수학 wipe 스크립트 운영 실행 — `node backend/dist/scripts/wipe-non-math.js --apply`. 이어서 `pnpm cli:kice-import --year=2025 && --year=2026` 로 수학 8 ProblemSet 재생성",
|
||
"Frontend 빌드 + standalone copy + `pm2 reload reloop-web reloop-api --update-env`",
|
||
"스모크 테스트 — 온보딩 `math-suneung` 플로우 + 단원 선택 저장, `ps` 플로우 + 더미 BOJ 핸들 sync, `/ps/search` 검색, `/exams` 에 수학 4단원 표시, `/review` 에 비수학 흔적 없음"
|
||
],
|
||
"recentEdits": [
|
||
".claude/sessions/.last_inbox_check",
|
||
".claude/state/plans-state.json",
|
||
".claude/state/tool-failure-counter.txt",
|
||
".claude/logs/subagent-history.jsonl",
|
||
".claude/state/breezing-timeline.jsonl",
|
||
".claude/state/compaction-events.jsonl",
|
||
".claude/state/contracts/7A.1.sprint-contract.json",
|
||
".claude/state/contracts/7A.2.sprint-contract.json",
|
||
".claude/state/contracts/7A.3.sprint-contract.json",
|
||
".claude/state/contracts/7A.4.sprint-contract.json",
|
||
".claude/state/contracts/7A.5.sprint-contract.json",
|
||
".claude/state/contracts/7A.6.sprint-contract.json",
|
||
".claude/state/contracts/7A.7.sprint-contract.json",
|
||
".claude/state/contracts/7A.8.sprint-contract.json",
|
||
".claude/state/contracts/7D.1.sprint-contract.json",
|
||
".claude/state/handoff-artifact.json",
|
||
".claude/state/phase7d-base-ref",
|
||
".claude/state/precompact-snapshot.json",
|
||
".claude/state/task-quality-gate.json",
|
||
".claude/state/worktree-info.json"
|
||
],
|
||
"metrics": null
|
||
} |