- POST /ai/suggest-checklist — 저장소 표시제목/설명 + 업무 제목/내용을 Claude 에 보내
카테고리별(필수/추가/보안) 할 일 항목을 JSON 으로 받아 파싱(코드펜스/잡텍스트 방어, 빈 결과 502)
- AiSuggestController + SuggestChecklistDto + AiService.suggestChecklist/parseSuggestions
- 프론트: TaskCreatePage 액션바 "취소 / AI 추천 / 지시 보내기" + 체크리스트 추천 패널
(카테고리별 항목 클릭 추가·전체 추가·중복 스킵), useAi.suggestChecklist(silent)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 컨텍스트에 저장소 목록 추가(RepoService.findAll), slug 빼고 표시 제목만 노출
- AI 응답 마크다운 렌더링: shared/utils/markdown.ts (escapeHtml 후 신뢰 태그만 조립, 의존성 0, XSS 안전 — 굵게/목록/코드/제목/링크)
- 시스템 프롬프트: 간결·이모지/사과 남발 금지·과도한 존칭 지양·마크다운 사용·저장소는 표시제목으로 안내
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
listAssigned/listIssued 가 to-many 조인 + 페이지네이션 정렬에서 DB 컬럼명
'task.due_date' 를 사용해, TypeORM distinct 경로가 컬럼 메타데이터를 못 찾아
"Cannot read properties of undefined (reading 'databaseName')" 로 크래시.
정렬 키를 엔티티 속성명 'task.dueDate' 로 변경 — 내 업무 화면 + AI 컨텍스트 동시 복구.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 시스템 프롬프트를 업무/Relay 한정으로 재작성 — 무관한 주제는 정중히 거절
- 매 요청 시 사용자 컨텍스트 주입: 오늘 날짜(KST) + 담당 업무 최대 30건(제목/상태/마감/체크리스트) + 지시 업무 수
- TaskService.listAssigned/listIssued 활용(AiModule→TaskModule 단방향), 조회 실패해도 채팅 진행
- callClaude(messages, system)로 시그니처 변경(매 메시지 최신 컨텍스트)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- AiConversation·AiMessage 엔티티(user/conversation CASCADE), 제목 자동 생성
- 대화 스코프 API: GET/POST /ai/conversations, GET/:id, POST/:id/messages, DELETE/:id (전부 본인 소유 스코프 → IDOR 차단)
- 클라는 새 메시지 1건만 전송, 서버가 DB 이력으로 Claude 호출(무상태 /ai/chat 폐기)
- Claude 실패 시 사용자 메시지·빈 대화 롤백(고아 데이터 방지)
- 프론트: AgentChatPanel 채팅↔지난 대화 목록 뷰(열기/삭제), ai.store 재작성(목록·현재 대화)
- 다른 기기·재로그인·새로고침에도 기록 유지
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 백엔드 modules/ai: POST /ai/chat — Anthropic Messages API 프록시(키 서버 env 한정, 30s 타임아웃, throttle 20/분, 미설정 503/외부오류 502)
- ChatDto 검증(role enum, content 8000자, 메시지 50개 상한), 모델 CLAUDE_MODEL(기본 claude-sonnet-4-6)
- 프론트 AgentChatPanel: 우측 슬라이드 채팅(말풍선/타이핑/에러/새 대화), plain text 렌더로 XSS 안전
- AppShell 헤더에 AI 버튼 상시 노출 — 모든 화면에서 열림
- 대화 이력은 stores/ai.store(싱글톤)에 보관 → 화면 이동에도 유지, '새 대화'로만 초기화
- TaskDetailPage 의 기존 목업 에이전트 패널 전부 제거(보안 L1 해소)
- env.example 에 CLAUDE_MODEL 추가
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- RefreshSession(패밀리) 테이블 도입 — 한 로그인=1행(currentJti/prevJti/expiresAt)
- /auth/refresh 회전형으로 교체: 현재 jti→새 jti 회전, 옛 토큰 재제출 시 재사용 탐지→패밀리 폐기(401)
- 동시 갱신 오탐 방지: 직전 jti 15초 유예 + 프론트 useApi single-flight refresh
- 로그아웃=해당 세션만 폐기, revokeAllSessions(전 기기) 준비
- tokenVersion·jwt-refresh.strategy·JwtRefreshGuard 제거(refresh는 가드 없이 서비스 직접 처리)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- backend/.env.development.example: CLAUDE_API_KEY 추가(dev 실제와 키 셋 일치)
- backend/.env.production.example: 소셜 OAuth·APP_API_URL/FRONTEND_URL·CLAUDE_API_KEY 추가(운영 placeholder)
- frontend/.env.*.example: 고유 키 없음 + VITE_API_BASE_URL docker 주입 설명 헤더 추가
실제 시크릿/인프라 값은 미포함(전부 placeholder).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- DTO 길이 바운드: 업무 content/assigneeIds/checklist ArrayMaxSize·요소 MaxLength, UpdateRepoDto.description MaxLength(300)
- 비밀번호 정책: 8~72자 + 영문·숫자 포함 강제, bcrypt rounds 10→12
- 그랜드페더링을 컷오프(2026-06-20) 이전 생성 계정으로 한정
- multer 업로드 오류를 400(VAL_001)로 매핑
- 운영 환경에서 CORS localhost:3000·Swagger /api-docs 노출 제외
(L1 에이전트 터미널 v-html 은 에이전트 작업 시 함께 처리 — 보류)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- User 엔티티 확장: passwordHash nullable, provider, emailVerified, 인증 토큰 해시/만료(select:false)
- 회원가입: 자동 로그인 폐지 → 미인증 계정 생성 + 인증 메일 발송(MailService 플레이스홀더, 백엔드 로그로 링크 출력)
- 이메일 인증: GET /auth/verify-email(해시 매칭+만료 검사) → 프론트로 리다이렉트, 재발송 엔드포인트
- 로그인 게이트: 미인증 로컬 403 차단, 소셜 전용 계정은 소셜 로그인 안내
- 소셜 OAuth: passport google/kakao 전략(키 미설정 시 자동 비활성), 이메일로 계정 매칭/생성
- 기존 로컬 계정 그랜드페더링(부팅 시 인증 완료 보정)
- 프론트: 가입 후 인증 메일 안내 화면, 로그인 소셜 버튼 실연동 + 인증 결과 배너
- env(APP_API_URL/FRONTEND_URL/GOOGLE_*/KAKAO_*) 문서화, 문서·계약 갱신
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- GET /repos/name-available?slug= 추가(:repoId 앞 선언, Relay slug 중복만 검사)
- 생성 폼: 형식 즉시 검사 + 350ms 디바운스 서버 조회로 상태별(빈칸/형식위반/확인중/사용가능/중복) 색·아이콘 표기
- 항상 떠 있던 정적 "사용 가능한 이름입니다" 표시 제거
- 조직 이니셜 소유자 아바타 제거
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RepoSyncService 의 @Cron 을 EVERY_HOUR→EVERY_10_MINUTES 로 변경.
Gitea 직접 변경(이름/공개범위/브랜치)의 Relay 반영 지연이 최대 1시간→10분으로 단축.
기동 직후 1회 동기화는 유지, 중복 실행 가드도 그대로.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
설정 탭/권한:
- findOne/update 응답에 현재 사용자 권한(canManage=admin, isOwner=소유자) 포함
(목록은 캐시·사용자 무관이라 미포함). RepoTabs 가 canManage 일 때만 설정 탭 노출,
비관리자의 설정 화면 직접 진입은 저장소 상세로 리다이렉트. 입력 비활성화 처리 제거.
- 저장소 삭제는 isOwner 일 때만(비소유자 admin 엔 비활성+안내).
설정 로직 버그/정리:
- update 가 이전 값과 비교해 실제 변경분만 활동/Gitea/캐시 반영
(무변경 저장 시 허위 'renamed'/'visibility_changed' 활동이 쌓이던 버그 수정).
- 위험구역의 미구현 'Gitea 저장소 이름 변경'(slug rename) 스텁 제거.
- 조직 owner 아바타(하드코딩 'M') 제거.
Gitea 역동기화(Gitea=SSOT):
- 동기화 매칭 시 slug(rename)·공개범위·기본 브랜치를 Gitea 기준으로 끌어옴
→ Gitea 에서 직접 바꿔도 Relay 가 따라가고 slugName 도 갱신돼 쓰기 경로 일치
(rename 후 옛 이름으로 쓰기 호출이 404 나던 desync 해소). 중복 매칭 가드 추가.
- 표시명/설명은 Gitea 단일 description 합본 구조라 Relay-SSOT 유지(외부 편집 시 깨짐 방지).
- GiteaService.updateRepo 에 default_branch 추가 + update 가 브랜치도 Gitea 에 푸시(양방향 일치).
검증: 백엔드 build/lint 0·20 tests, 프론트 type-check/lint/build 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
task.status_changed 를 일반 라벨('승인 대기로 변경')이 아닌 행위 중심 문구로
파생(statusFrom→statusTo): 시작/다시 시작·승인을 요청·수정을 요청·완료(승인).
업무 상세 활동 탭(task.store)·알림과 동일 표현으로 일관화. 안 쓰게 된
taskStatusLabel·TaskStatus import 제거.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 백엔드: User.avatarUrl(nullable) 추가 → PublicUser/toPublic 로 모든 유저 응답에 노출.
- 프론트: 공용 UserAvatar(이미지 있으면 img, 없으면 사람 실루엣 placeholder)로
전 화면 아바타 통일(상단바·저장소·멤버·담당자·활동·내업무·댓글·AI패널).
화면마다 달랐던 색상+이니셜/하드코딩 청록 아바타 제거.
- 죽은 코드 정리: 색상·이니셜 시스템(avatarColor/initialOf/AvatarColor 유틸·타입,
User/RepoActivityItem 의 initial/color, 전역·컴포넌트 .av-a~.av-f CSS) +
미사용 정적 목업 데이터(USERS/REPOS/REPO_TASKS/TASK_DETAIL/MY_TASKS_*/REPO_MEMBERS/
REPO_ACTIVITY 등) 전부 삭제. relay.mock.ts 는 뷰 타입 + countActive 만 유지.
avatar_url 컬럼은 dev synchronize 가 추가. 업로드(실제 사진 설정)는 추후 단계.
검증: 백엔드 build 0, 프론트 type-check/lint/build 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
미인증으로 보호 라우트 진입 시 가드가 붙이던 redirect 쿼리로 로그인 후 이전 화면에
복귀하던 동작 제거. LoginPage 는 항상 /repos 로 이동하고, 가드는 redirect 쿼리를
더 이상 보존하지 않는다(딥링크 복귀 제거).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
알림(8-2):
- Notification 모듈 + SSE 스트림(@Sse, @SkipTransform 으로 표준 래퍼 우회).
- 12종 알림 적재(업무 지시/해제/승인요청/승인/수정요청/시작/마감변경/삭제/댓글·답글,
멤버 초대/역할변경/제거). 행위자 제외 + 중복 dedup. AppShell 종(미읽음 뱃지·드롭다운).
- 실시간 fan-out: REDIS_HOST 시 ioredis pub/sub(다중 인스턴스), 미설정 시 인메모리 폴백.
Redis 캐싱(8-3):
- CacheModule → @keyv/redis(미설정 시 인메모리 폴백). 저장소 목록을 버전 네임스페이스로
캐시, 생성/수정/삭제·동기화·멤버 초대/제거 시 무효화(공유 버전 키라 다중 인스턴스 정합).
- enableShutdownHooks 로 종료 시 Redis 연결 graceful close.
검증: 백엔드 build/lint 0·20 tests, 프론트 build/lint/type-check 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 저장소 업무 목록(RepoDetailPage): 세그먼트/검색을 백엔드로 이전(클라 필터 제거),
응답 counts 로 세그먼트 배지, 더보기(오프셋) 추가. task.store 에 total/counts/hasMore/loadMore.
- 멤버: member.store.fetchList 가 페이지네이션 API 를 끝까지 순회해 전체 로드
(역할/검색 카운트 전수 + TaskCreatePage 담당자 후보 보장).
- 타입 신설: TaskSegment/TaskSegmentCounts/ApiRepoTaskListResult.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
페이지네이션 도입 후 백엔드 목록 API 가 { items, total, ... } 를 반환하는데
프론트 멤버·업무 소비처가 배열로 기대해 list.map 이 깨지고, 멤버 탭과 업무
목록이 비어 보이던 회귀를 수정.
- backend(task): 업무 목록 쿼리에서 to-many 조인 컬럼(checklist.sort_order)
으로 정렬해 페이지네이션 시 TypeORM 이 터지던 문제 제거(task.seq 만 정렬).
목록 응답은 체크리스트 [완료/전체] 수만 쓰므로 항목 순서가 불필요.
- frontend(member/task): list 컴포저블이 Paginated<T> 를 반환하도록 맞추고
스토어에서 .items 로 언래핑(저장소·내업무와 동일 계약).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
HttpException 이 아닌 예외를 Logger.error 두 번째 인자(스택 문자열 자리)에
Error 객체째로 넘겨, nest-winston 직렬화 과정에서 message/stack 이 사라지고
`{ stack: [ {} ] }` 로만 찍히던 문제를 수정. 메시지는 message 로, 스택은
문자열로 분리해 실제 원인이 로그에 남도록 한다.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 멤버 목록/초대/역할변경/제거 프론트 연동(RepoMembersPage), 초대 자동완성
- GET /users 사용자 검색 API + UserController
- RepoMembersPage 역할 변경 드롭다운 잘림(overflow) 수정
- InviteMember/UpdateMember DTO, RepoMember 엔티티 정비
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- GiteaRepoBar 컴포넌트(클론 주소 복사 + Gitea 열기)를 RepoSettingsPage 에 적용
- repo.store 뷰에 cloneUrl/htmlUrl/giteaMissing 매핑
- repo-sync: import 저장소 owner-admin 자동 지정(REPO_IMPORT_ADMIN_EMAIL)
- env example·docker-compose.dev 에 Gitea/import-admin 키 정비
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 루트 .env.{development,production}.example 신규 (비밀값 placeholder 마스킹)
- frontend .env.{development,production}.example 신규 (docker build arg 주입 설명)
- backend example 의 단독 실행(non-Docker) 예시 블록 제거
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 프로젝트 설명을 생성 필수 항목으로 변경(CreateRepoDto.description required + 프론트 검증)
- 템플릿 복제: useTemplate=true 면 Gitea generate API 로 GITEA_TEMPLATE_OWNER/REPO
(기본 TtiPo/project-base) 저장소를 복제 생성(브랜치는 템플릿을 따름)
- GET /repos/owner → /repos/meta 로 확장({ owner, template }) — 생성폼 메타 일괄 제공
- 프론트 생성폼: 가짜 템플릿 목록 제거 → 실제 템플릿 옵션, 설명 필수 UI
- env.example/ api-contract/ 진행현황 문서 갱신
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
생성 폼/목록의 하드코딩된 'marketing-team' 을 실제 설정값으로 교체한다.
- 백엔드: GET /repos/owner ({ owner }) — GITEA_ORG(미연동 시 기본 상수) 반환
- 프론트: useRepo.owner() + repo.store owner/fetchOwner(캐시)
- RepoCreatePage 소유자 고정 표시, RepoListPage 배너를 실제 조직명으로
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
외부 Gitea 인스턴스의 조직에 저장소를 자동 동기화한다.
- GiteaService: /api/v1 클라이언트(내장 fetch, 새 의존성 없음), enabled 게이트
- Repo 엔티티에 연동 컬럼(giteaRepoId/giteaFullName/cloneUrl/htmlUrl) 추가
- RepoService 생성/수정/삭제에 Gitea 동기화 와이어링(생성 우선·실패 시 롤백)
- GITEA_* env 문서화(.env.example), api-contract/진행현황 문서 갱신
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>