v0.2.11 Cut D — FTS5 search + 회고 view (F19 A+D) #29

Merged
altair823 merged 10 commits from worktree-v0211-cut-d-fts5-review into main 2026-05-09 15:52:19 +00:00
Owner

Summary

v0.2.11 Cut D — F19 (recall 메커니즘) 의 A+D 옵션 적용. recall 핵심 가치 도달 — 누적 노트에서 키워드 검색 + 일/주/월 회고 view.

  • F19-A FTS5 search: SQLite FTS5 가상 테이블 notes_fts (raw_text/ai_title/ai_summary/tags). m007 마이그레이션 + AFTER INSERT/UPDATE/DELETE 트리거 (notes 컬럼 자동 sync) + tags 는 rebuildFtsTagsForNote single write path. inbox 헤더 search box (debounce 200ms) + 결과 렌더 분기. 빈 query → 기본 list. 기본 trashed 제외 + status filter 가능.
  • F19-D 회고 view: 일/주/월 라우트 + ReviewView 컴포넌트. period 별 cutoff (KST 자정) + totalCount + tagCounts (DESC) + dueProgress (passed/pending KST today 기준) + 최근 50 NoteCard. 헤더 dropdown 진입점. useInbox.view enum 에 review-daily / review-weekly / review-monthly 추가.
  • single write path 강제 (Cut C 확립 + Cut D 보강): tags 변경하는 모든 path (updateAiResult / updateUserAiFields / importNote) 가 rebuildFtsTagsForNote(noteId) 호출 — final review 단계에서 importNote 누락 발견 → 패치.
  • AI 정책 무변: AiWorker findById(id).rawText source 코드 무수정. raw_text 가변 (Cut C) 의 trigger AFTER UPDATE 가 자동 sync — 회귀 검증.

변경 내역 (10 commits)

Phase 0: 문서

  • 1104a8c docs(plan) — Cut D plan + spec m006→m007 정정 (Cut C 가 m006 선점) + ai_title/ai_summary + note_tags JOIN 정정

Phase 1: schema

  • 19edeab Task 1 — m007 마이그레이션 (notes_fts FTS5 + 트리거 3 + backfill, status != 'trashed')

Phase 2: repo

  • 726d155 Task 2 — rebuildFtsTagsForNote 헬퍼 + tags 변경 path 통합 (updateAiResult / updateUserAiFields)
  • e60a2a2 Tasks 3-5 — ftsHelpers (sanitize + computeCutoff) + NoteRepository.search + reviewAggregate

Phase 3: 와이어업

  • 143684c Task 6 — InboxApi.search + reviewAggregate (types + IPC + preload)

Phase 4: store

  • f5e4313 Task 7 — store search + reviewData state + actions + view enum 확장

Phase 5: UI

  • be125b8 Task 8 — SearchBox (debounce 200ms) + 헤더 mount + inbox 결과 렌더 분기
  • 9feb712 Task 9 — ReviewView (period 라벨 + tag bar + due progress + 최근 노트) + 헤더 dropdown 진입점

Phase 6: release

  • 5801a98 chore(release) — F19 promoted 마킹 + version 0.2.10→0.2.11

Phase 7: final review fix

  • 735d549 fix(v0211) — importNoterebuildFtsTagsForNote 호출 (final review 발견: F5 import 후 tag keyword 검색 매칭 안 되는 회귀). 회귀 test 2건 (insert / fork path) 추가. NoteRepository 의 note_tags INSERT path 3곳 모두 헬퍼 호출 보장 — invariant 회복.

테스트 / 빌드

  • 단위: 569 → 608 pass (+39):
    • m007 마이그레이션 6
    • rebuildFtsTagsForNote (updateAiResult / updateUserAiFields) 2
    • ftsHelpers (sanitize + computeCutoff) 7
    • search 6
    • reviewAggregate 5
    • IPC handlers 3
    • store 3
    • SearchBox 2
    • ReviewView 3
    • importNote tags sync (final review) 2
  • typecheck: 0 errors
  • e2e: 세션 내 미수행 — worktree node_modules 비어 있어 prebuild path 실패 (Cut C 동일). 본 cut 의 변경은 헤더 search box / 회고 dropdown / ReviewView 라우트 — capture/onboarding flow 무관, 머지 후 main 에서 검증 권장.
  • 빌드 산출물: 후속 release 단계에서 Windows exe + macOS dmg + Linux AppImage/deb

Schema 변경

  • m007: notes_fts FTS5 가상 테이블 (note_id UNINDEXED + raw_text + ai_title + ai_summary + tags + tokenize='unicode61'). AFTER INSERT/UPDATE/DELETE 트리거 3개 (notes 컬럼 자동 sync, tags 는 헬퍼 path). 기존 status != 'trashed' 노트 backfill (note_tags JOIN GROUP_CONCAT). transaction 안 단일 exec (runMigrations wrap)

메모리 정책 갱신 (Cut D 머지 후 적용)

  • search source = notes.raw_text/ai_title/ai_summary/tags csv (FTS5 인덱스 latest only — revision X). Cut C 의 raw_text 가변 정책과 일관 (옛 revision 검색 X)
  • single write path 강제 (Cut D 보강): tags 변경 path = updateAiResult / updateUserAiFields / importNote 3곳, 모두 rebuildFtsTagsForNote 호출 보장 — invariant 검증 완료
  • 회고 cutoff = KST 자정 ISO (computeCutoff). dueProgress 비교 = KST today (kstTodayIso)
  • trashed 노트 FTS row 잔존 + query-level 필터 (별도 cleanup 안 함 — YAGNI)

Risk 잔재 (final review)

  • FTS5 한국어 token 정확도: unicode61 의 word boundary 가 한국어에서 부정확 — dogfood 검증 필요. 부족 시 mecab-ko / trigram tokenize 검토 (별도 cut)
  • FTS5 operator words OR/AND/NEAR: sanitizeFtsQuery"*():만 strip. 회의 OR 결재 같은 mixed query 가 Boolean OR 로 동작 — dogfood 시 사용자 헷갈림 가능. 향후 cut 에서 lowercase 정규화 또는 doc 정정
  • revision unlimited 누적 (Cut C 잔재): 메모 1개당 100+ revision 시 DB bloat. cap 정책 별도 cut
  • m007 backfill timing: 본인 v0.2.10 사용자 DB 기준 노트 수 작아 단일 statement 안전. 큰 DB 환경 미검증 (Cut C 동일 risk pattern)
  • trigger overhead: notes 모든 UPDATE 마다 trigger 발동. dogfood 규모에서 무시 가능

Test Plan

  • 헤더 search box 에 키워드 입력 → 200ms 후 결과 list 갱신 + 빈 값 → 기본 list
  • FTS5 special char (", *, (, ), :) 입력 시 crash 없이 안전 처리
  • 한국어 토큰 매칭 (회의/기획/결재 등) — dogfood 시 정확도 측정
  • 헤더 회고 dropdown → 일/주/월 진입 → totalCount + tag bar + dueProgress + 최근 노트 정상
  • dueProgress passed/pending 이 KST today 기준 정확 (오늘 due 는 pending)
  • 새 노트 추가 시 trigger 가 notes_fts INSERT — 즉시 검색 가능
  • AI 결과 적용 시 (updateAiResult) tags csv 가 notes_fts.tags 에 sync — tag keyword 검색 매칭
  • F5 import (importNote) 시 tags csv sync — import 한 노트도 즉시 검색 가능 (final review fix 검증)
  • Cut C raw_text 편집 시 trigger 자동 sync — 새 raw_text 로 검색 가능
  • trashed 노트는 search 기본 결과에서 제외 + status='trashed' 명시 시만 노출
  • m007 마이그레이션이 기존 v0.2.10 사용자 DB 에서 정상 적용 (note_tags / media / pending_jobs / note_revisions 보존 + active/completed/archived 노트만 backfill)
  • macOS dmg / Linux AppImage/deb 빌드 + Linux VM smoke

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

## Summary v0.2.11 Cut D — F19 (recall 메커니즘) 의 A+D 옵션 적용. recall 핵심 가치 도달 — 누적 노트에서 키워드 검색 + 일/주/월 회고 view. - **F19-A FTS5 search**: SQLite FTS5 가상 테이블 `notes_fts` (raw_text/ai_title/ai_summary/tags). m007 마이그레이션 + AFTER INSERT/UPDATE/DELETE 트리거 (notes 컬럼 자동 sync) + tags 는 `rebuildFtsTagsForNote` single write path. inbox 헤더 search box (debounce 200ms) + 결과 렌더 분기. 빈 query → 기본 list. 기본 trashed 제외 + status filter 가능. - **F19-D 회고 view**: 일/주/월 라우트 + ReviewView 컴포넌트. period 별 cutoff (KST 자정) + totalCount + tagCounts (DESC) + dueProgress (passed/pending KST today 기준) + 최근 50 NoteCard. 헤더 dropdown 진입점. `useInbox.view` enum 에 `review-daily` / `review-weekly` / `review-monthly` 추가. - **single write path 강제** (Cut C 확립 + Cut D 보강): tags 변경하는 모든 path (`updateAiResult` / `updateUserAiFields` / `importNote`) 가 `rebuildFtsTagsForNote(noteId)` 호출 — final review 단계에서 importNote 누락 발견 → 패치. - **AI 정책 무변**: AiWorker `findById(id).rawText` source 코드 무수정. raw_text 가변 (Cut C) 의 trigger AFTER UPDATE 가 자동 sync — 회귀 검증. ## 변경 내역 (10 commits) ### Phase 0: 문서 - `1104a8c` docs(plan) — Cut D plan + spec m006→m007 정정 (Cut C 가 m006 선점) + ai_title/ai_summary + note_tags JOIN 정정 ### Phase 1: schema - `19edeab` Task 1 — m007 마이그레이션 (`notes_fts` FTS5 + 트리거 3 + backfill, status != 'trashed') ### Phase 2: repo - `726d155` Task 2 — `rebuildFtsTagsForNote` 헬퍼 + tags 변경 path 통합 (updateAiResult / updateUserAiFields) - `e60a2a2` Tasks 3-5 — `ftsHelpers` (sanitize + computeCutoff) + `NoteRepository.search` + `reviewAggregate` ### Phase 3: 와이어업 - `143684c` Task 6 — `InboxApi.search` + `reviewAggregate` (types + IPC + preload) ### Phase 4: store - `f5e4313` Task 7 — store search + reviewData state + actions + view enum 확장 ### Phase 5: UI - `be125b8` Task 8 — SearchBox (debounce 200ms) + 헤더 mount + inbox 결과 렌더 분기 - `9feb712` Task 9 — ReviewView (period 라벨 + tag bar + due progress + 최근 노트) + 헤더 dropdown 진입점 ### Phase 6: release - `5801a98` chore(release) — F19 promoted 마킹 + version 0.2.10→0.2.11 ### Phase 7: final review fix - `735d549` fix(v0211) — `importNote` 가 `rebuildFtsTagsForNote` 호출 (final review 발견: F5 import 후 tag keyword 검색 매칭 안 되는 회귀). 회귀 test 2건 (insert / fork path) 추가. NoteRepository 의 `note_tags` INSERT path 3곳 모두 헬퍼 호출 보장 — invariant 회복. ## 테스트 / 빌드 - 단위: 569 → **608 pass** (+39): - m007 마이그레이션 6 - rebuildFtsTagsForNote (updateAiResult / updateUserAiFields) 2 - ftsHelpers (sanitize + computeCutoff) 7 - search 6 - reviewAggregate 5 - IPC handlers 3 - store 3 - SearchBox 2 - ReviewView 3 - importNote tags sync (final review) 2 - typecheck: **0 errors** - e2e: **세션 내 미수행** — worktree node_modules 비어 있어 prebuild path 실패 (Cut C 동일). 본 cut 의 변경은 헤더 search box / 회고 dropdown / ReviewView 라우트 — capture/onboarding flow 무관, 머지 후 main 에서 검증 권장. - 빌드 산출물: 후속 release 단계에서 Windows exe + macOS dmg + Linux AppImage/deb ## Schema 변경 - m007: `notes_fts` FTS5 가상 테이블 (note_id UNINDEXED + raw_text + ai_title + ai_summary + tags + tokenize='unicode61'). AFTER INSERT/UPDATE/DELETE 트리거 3개 (notes 컬럼 자동 sync, tags 는 헬퍼 path). 기존 `status != 'trashed'` 노트 backfill (note_tags JOIN GROUP_CONCAT). transaction 안 단일 exec (runMigrations wrap) ## 메모리 정책 갱신 (Cut D 머지 후 적용) - **search source = `notes.raw_text`/`ai_title`/`ai_summary`/tags csv (FTS5 인덱스 latest only — revision X)**. Cut C 의 raw_text 가변 정책과 일관 (옛 revision 검색 X) - **single write path 강제 (Cut D 보강)**: tags 변경 path = `updateAiResult` / `updateUserAiFields` / `importNote` 3곳, 모두 `rebuildFtsTagsForNote` 호출 보장 — invariant 검증 완료 - **회고 cutoff = KST 자정 ISO** (computeCutoff). dueProgress 비교 = KST today (`kstTodayIso`) - **trashed 노트 FTS row 잔존 + query-level 필터** (별도 cleanup 안 함 — YAGNI) ## Risk 잔재 (final review) - **FTS5 한국어 token 정확도**: unicode61 의 word boundary 가 한국어에서 부정확 — dogfood 검증 필요. 부족 시 mecab-ko / trigram tokenize 검토 (별도 cut) - **FTS5 operator words `OR/AND/NEAR`**: `sanitizeFtsQuery` 가 `"*():`만 strip. `회의 OR 결재` 같은 mixed query 가 Boolean OR 로 동작 — dogfood 시 사용자 헷갈림 가능. 향후 cut 에서 lowercase 정규화 또는 doc 정정 - **revision unlimited 누적** (Cut C 잔재): 메모 1개당 100+ revision 시 DB bloat. cap 정책 별도 cut - **m007 backfill timing**: 본인 v0.2.10 사용자 DB 기준 노트 수 작아 단일 statement 안전. 큰 DB 환경 미검증 (Cut C 동일 risk pattern) - **trigger overhead**: notes 모든 UPDATE 마다 trigger 발동. dogfood 규모에서 무시 가능 ## Test Plan - [ ] 헤더 search box 에 키워드 입력 → 200ms 후 결과 list 갱신 + 빈 값 → 기본 list - [ ] FTS5 special char (`"`, `*`, `(`, `)`, `:`) 입력 시 crash 없이 안전 처리 - [ ] 한국어 토큰 매칭 (회의/기획/결재 등) — dogfood 시 정확도 측정 - [ ] 헤더 회고 dropdown → 일/주/월 진입 → totalCount + tag bar + dueProgress + 최근 노트 정상 - [ ] dueProgress passed/pending 이 KST today 기준 정확 (오늘 due 는 pending) - [ ] 새 노트 추가 시 trigger 가 notes_fts INSERT — 즉시 검색 가능 - [ ] AI 결과 적용 시 (updateAiResult) tags csv 가 notes_fts.tags 에 sync — tag keyword 검색 매칭 - [ ] F5 import (importNote) 시 tags csv sync — import 한 노트도 즉시 검색 가능 (final review fix 검증) - [ ] Cut C raw_text 편집 시 trigger 자동 sync — 새 raw_text 로 검색 가능 - [ ] trashed 노트는 search 기본 결과에서 제외 + `status='trashed'` 명시 시만 노출 - [ ] m007 마이그레이션이 기존 v0.2.10 사용자 DB 에서 정상 적용 (note_tags / media / pending_jobs / note_revisions 보존 + active/completed/archived 노트만 backfill) - [ ] macOS dmg / Linux AppImage/deb 빌드 + Linux VM smoke 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
altair823 added 10 commits 2026-05-09 15:48:22 +00:00
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ftsHelpers: sanitizeFtsQuery (FTS5 special char escape) + computeCutoff (period → KST 자정)
- search: notes_fts MATCH + status filter + rank order + sanitize + 빈 query → []
- reviewAggregate: period 별 totalCount/recentNotes(50)/tagCounts(DESC)/dueProgress(passed/pending)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- F19 promoted ( v0.2.11 Cut D — A+D 옵션)
- version 0.2.10 → 0.2.11 (package.json + package-lock.json)
- 단위 569 → 606 (m007 6 + tags sync 2 + ftsHelpers 7 + search 6 + reviewAggregate 5 + IPC 3 + store 3 + SearchBox 2 + ReviewView 3 = 37 신규)
- typecheck 0 errors
final code review 발견: F5 import path 가 note_tags INSERT 후 notes_fts.tags 갱신
안 해서 import 한 노트의 tag 가 keyword 검색에서 매칭 안 되는 회귀.

Cut C 의 importNote capture revision 누락 패턴과 동일 — single write path
정책 (Cut D 도입) 의 강제 검사 누락. importNote transaction 끝에서 호출하도록
fix + 회귀 test 2건 (insert path / fork path) 추가.

NoteRepository 안 note_tags INSERT path 는 updateAiResult / updateUserAiFields /
importNote 3곳, 셋 다 rebuildFtsTagsForNote 호출 보장 — invariant 회복.
claude-reviewer-01 approved these changes 2026-05-09 15:50:11 +00:00
claude-reviewer-01 left a comment
Member

코드 리뷰 — v0.2.11 Cut D

Scope: 10 commits (1104a8c..735d549)

Spec coverage 100%

Cut D design 의 모든 섹션이 task 매핑됨. F19-A (FTS5 search) + F19-D (일/주/월 회고 view).

spec § 구현
§3-1 m007 schema m007_fts.ts — virtual table + index + backfill (status != 'trashed', note_tags JOIN GROUP_CONCAT)
§3-2 트리거 3 AFTER triggers (INSERT/UPDATE/DELETE on notes) — raw_text/ai_title/ai_summary 자동 sync. tags 는 헬퍼 path 강제
§3-3 search sanitizeFtsQuery + notes_fts MATCH + ORDER BY rank + 기본 trashed 제외 + 빈 query → []
§3-4 search box SearchBox.tsx — 200ms debounce + clearSearch on empty
§3-5 IPC inbox:search + inbox:review-aggregate (period validation)
§4-1 회고 라우트 view enum 확장 + setViewloadReview 자동 발동
§4-2 ReviewView period 라벨 + totalCount + tagBar + dueProgress + 최근 50 NoteCard
§4-3 reviewAggregate 4 필드, period cutoff filter, status != 'trashed', tagCounts via note_tags JOIN, dueProgress KST today 기준
§5 테스트 +39 신규 단위 (목표 23 초과 — 회귀 + final review fix 포함)

코드 품질

Strengths

  • Pure-function helpers (ftsHelpers.ts): sanitizeFtsQuery / computeCutoff 분리 → 단위 테스트 용이 (7건). KST cutoff 산식 verified (2026-05-10T05:30Z → 2026-05-09T15:00Z daily).
  • Single write path discipline: rebuildFtsTagsForNote (NoteRepository.ts:942) private + updateAiResult / updateUserAiFields / importNote (final review fix) 3 path 모두 동일 transaction 안 호출. tags 갱신 invariant 강제.
  • 트리거 design 분리: notes 컬럼 (raw_text/ai_title/ai_summary) 은 트리거 자동 sync, tags 는 JOIN 결과 = 헬퍼 manual path. 이유 명확 (note_tags INSERT/DELETE 가 다른 노트 row 재계산 트리거 부담) + 주석 충실.
  • trashed FTS row 처리: query-level filter (AND n.status != 'trashed') — cleanup 안 함. YAGNI 적절. 명시적으로 default vs status 옵션 두 path 모두 검증.
  • AI 정책 보존: AiWorker findById(id).rawText source 코드 무수정. Cut C raw_text 가변 정책의 trigger AFTER UPDATE 가 자동 sync — 회귀 검증 포함.
  • review aggregate 산식 정확: dueProgress 의 passed/pending 이 KST today (kstTodayIso) 기준 — 5건 reviewAggregate 테스트로 검증.

Final review follow-up 적용 (735d549)

initial final review (Opus, 1차) 에서 발견된 Important issue:

  • importNoterebuildFtsTagsForNote 호출 안 함 → F5 import 후 tag keyword 검색 매칭 안 되는 회귀

→ Fix:

  • importNote transaction 끝에서 헬퍼 호출 (NoteRepository.ts:856)
  • 회귀 test 2건 (insert path / fork path) 추가 (tests/unit/NoteRepository.test.ts:1108-1166)
  • 패턴 인식 — Cut C 의 importNote capture revision 누락과 동일 패턴 (single write path 누락). note_tags INSERT path = 3곳 (updateAiResult / updateUserAiFields / importNote), 모두 헬퍼 호출 보장 검증

re-verification 결과:

  • 단일 write path invariant 회복 — note_tags 변경하는 모든 path 가 rebuildFtsTagsForNote 호출
  • 608/608 unit PASS, typecheck 0
  • bounded scope (importNote + 회귀 test 만 변경, 다른 path 무영향)

Architecture

  • 단위 분해 명확 (migration / helper / repository / IPC / preload / store / component / view 8계층 분리)
  • 책임 명확성 — sanitizeFtsQuery = pure / search = JOIN+rank / reviewAggregate = 4 read query / rebuildFtsTagsForNote = post-mutation sync
  • 일관성 — 기존 MoveStatusModal / RevisionHistoryModal modal pattern 와 다른 컴포넌트 (SearchBox / ReviewView) 도 zustand 패턴 일관

Risk 잔재 (final review 분석)

  • FTS5 한국어 token 정확도: unicode61 word boundary 가 한국어에서 부정확 — dogfood 검증 필요. 부족 시 mecab-ko / trigram 별도 cut
  • FTS5 operator words OR/AND/NEAR: sanitizeFtsQuery"*():만 strip. mixed query (한+영) 에서 Boolean 동작 — 향후 operator 정규화 또는 doc 추가
  • revision 무한 누적 (Cut C 잔재): cap 정책 별도 cut
  • m007 backfill on large tables: 단일 INSERT...SELECT + GROUP_CONCAT 서브쿼리. 본인 사용자 DB scale (수백) safe. 큰 DB 환경 미검증
  • trigger overhead: 모든 notes UPDATE → trigger. dogfood scale 무시
  • e2e 본 세션 미수행: worktree node_modules 비어 있음. 본 cut diff = 헤더 SearchBox + 회고 dropdown + ReviewView 라우트 — capture/onboarding flow 무관, 머지 후 main 검증 권장

Sub-review 트레일

  • Task 1 (m007) — Sonnet implementer + spec verbatim
  • Task 2 (rebuildFtsTagsForNote) — Sonnet implementer + 회귀 test . importNote gap 명시 보고 (final review 발견 사전 알림)
  • Tasks 3-5 (ftsHelpers + search + reviewAggregate) — Sonnet batch implementer , 18 신규 test
  • Task 6 (types + IPC + preload) — Sonnet implementer , 3 IPC test (period validation 포함)
  • Task 7 (store) — Sonnet implementer , view enum + 4 actions + 3 test
  • Task 8 (SearchBox + 헤더) — Sonnet implementer , hooks-of-rules 보강 (subscribe 위치 정정)
  • Task 9 (ReviewView) — Sonnet implementer , NoteCard 의 inboxApi mock 자동 보강
  • Final code review (Opus, 1차) — ⚠ Approved with follow-up (importNote gap)
  • Final fix re-verification 608/608 + typecheck 0

머지 권장

  1. PR #29 머지
  2. tag v0.2.11 + npm run dist:win → Windows exe 빌드
  3. Gitea release v0.2.11 + exe attach
  4. macOS host: dist:mac + dist:linux 후 dmg/AppImage/deb 추가 attach
  5. 메모리 정책 갱신 — search source = latest (revision X) + single write path 강제 정책 보강 (3 path 검증)
  6. dogfood 1주 soak — search 사용 빈도 + 한국어 token 정확도 + 회고 view 사용 빈도 → v0.2.12 또는 Cut E 진입 결정

Overall

Ready to merge. Spec 100% coverage, 569 → 608 unit (+39) + typecheck 0, m007 마이그레이션 외래키 안전 + trigger 자동 sync, single write path 정책 invariant 검증 (3 path 전수 점검), v0.2.6 #10 fix / Cut B 'disabled' / Cut C revision API 모두 호환. final review follow-up 완료. Cut E (v0.3.0 — F21 양방향 sync) 진입 가능.

🤖 Reviewed by Claude Opus 4.7 (1M context) with Sonnet/Haiku sub-agents per subagent-driven-development skill

## 코드 리뷰 — v0.2.11 Cut D **Scope**: 10 commits (1104a8c..735d549) ### Spec coverage ✅ 100% [Cut D design](docs/superpowers/specs/2026-05-09-v0211-cut-d-design.md) 의 모든 섹션이 task 매핑됨. F19-A (FTS5 search) + F19-D (일/주/월 회고 view). | spec § | 구현 | |---|---| | §3-1 m007 schema | `m007_fts.ts` — virtual table + index + backfill (status != 'trashed', note_tags JOIN GROUP_CONCAT) | | §3-2 트리거 | 3 AFTER triggers (INSERT/UPDATE/DELETE on notes) — raw_text/ai_title/ai_summary 자동 sync. tags 는 헬퍼 path 강제 | | §3-3 search | `sanitizeFtsQuery` + `notes_fts MATCH` + ORDER BY rank + 기본 trashed 제외 + 빈 query → [] | | §3-4 search box | `SearchBox.tsx` — 200ms debounce + clearSearch on empty | | §3-5 IPC | `inbox:search` + `inbox:review-aggregate` (period validation) | | §4-1 회고 라우트 | `view` enum 확장 + `setView` 의 `loadReview` 자동 발동 | | §4-2 ReviewView | period 라벨 + totalCount + tagBar + dueProgress + 최근 50 NoteCard | | §4-3 reviewAggregate | 4 필드, period cutoff filter, status != 'trashed', tagCounts via note_tags JOIN, dueProgress KST today 기준 | | §5 테스트 | +39 신규 단위 (목표 23 초과 — 회귀 + final review fix 포함) | ### 코드 품질 **Strengths** - **Pure-function helpers** (`ftsHelpers.ts`): `sanitizeFtsQuery` / `computeCutoff` 분리 → 단위 테스트 용이 (7건). KST cutoff 산식 verified (2026-05-10T05:30Z → 2026-05-09T15:00Z daily). - **Single write path discipline**: `rebuildFtsTagsForNote` (`NoteRepository.ts:942`) `private` + `updateAiResult` / `updateUserAiFields` / `importNote` (final review fix) 3 path 모두 동일 transaction 안 호출. tags 갱신 invariant 강제. - **트리거 design 분리**: notes 컬럼 (raw_text/ai_title/ai_summary) 은 트리거 자동 sync, tags 는 JOIN 결과 = 헬퍼 manual path. 이유 명확 (note_tags INSERT/DELETE 가 다른 노트 row 재계산 트리거 부담) + 주석 충실. - **trashed FTS row 처리**: query-level filter (`AND n.status != 'trashed'`) — cleanup 안 함. YAGNI 적절. 명시적으로 default vs `status` 옵션 두 path 모두 검증. - **AI 정책 보존**: AiWorker `findById(id).rawText` source 코드 무수정. Cut C raw_text 가변 정책의 trigger AFTER UPDATE 가 자동 sync — 회귀 검증 포함. - **review aggregate 산식 정확**: dueProgress 의 passed/pending 이 KST today (`kstTodayIso`) 기준 — 5건 reviewAggregate 테스트로 검증. **Final review follow-up 적용** (`735d549`) initial final review (Opus, 1차) 에서 발견된 Important issue: - ❌ `importNote` 가 `rebuildFtsTagsForNote` 호출 안 함 → F5 import 후 tag keyword 검색 매칭 안 되는 회귀 → Fix: - ✅ `importNote` transaction 끝에서 헬퍼 호출 (`NoteRepository.ts:856`) - ✅ 회귀 test 2건 (insert path / fork path) 추가 (`tests/unit/NoteRepository.test.ts:1108-1166`) - ✅ 패턴 인식 — Cut C 의 importNote capture revision 누락과 동일 패턴 (single write path 누락). `note_tags` INSERT path = 3곳 (updateAiResult / updateUserAiFields / importNote), 모두 헬퍼 호출 보장 검증 re-verification 결과: - 단일 write path invariant 회복 — `note_tags` 변경하는 모든 path 가 `rebuildFtsTagsForNote` 호출 - 608/608 unit PASS, typecheck 0 - bounded scope (`importNote` + 회귀 test 만 변경, 다른 path 무영향) ### Architecture - 단위 분해 명확 (migration / helper / repository / IPC / preload / store / component / view 8계층 분리) - 책임 명확성 — `sanitizeFtsQuery` = pure / `search` = JOIN+rank / `reviewAggregate` = 4 read query / `rebuildFtsTagsForNote` = post-mutation sync - 일관성 — 기존 `MoveStatusModal` / `RevisionHistoryModal` modal pattern 와 다른 컴포넌트 (`SearchBox` / `ReviewView`) 도 zustand 패턴 일관 ### Risk 잔재 (final review 분석) - **FTS5 한국어 token 정확도**: unicode61 word boundary 가 한국어에서 부정확 — dogfood 검증 필요. 부족 시 mecab-ko / trigram 별도 cut - **FTS5 operator words `OR/AND/NEAR`**: `sanitizeFtsQuery` 가 `"*():`만 strip. mixed query (한+영) 에서 Boolean 동작 — 향후 operator 정규화 또는 doc 추가 - **revision 무한 누적 (Cut C 잔재)**: cap 정책 별도 cut - **m007 backfill on large tables**: 단일 INSERT...SELECT + GROUP_CONCAT 서브쿼리. 본인 사용자 DB scale (수백) safe. 큰 DB 환경 미검증 - **trigger overhead**: 모든 notes UPDATE → trigger. dogfood scale 무시 - **e2e 본 세션 미수행**: worktree node_modules 비어 있음. 본 cut diff = 헤더 SearchBox + 회고 dropdown + ReviewView 라우트 — capture/onboarding flow 무관, 머지 후 main 검증 권장 ### Sub-review 트레일 - **Task 1 (m007)** — Sonnet implementer + spec verbatim ✅ - **Task 2 (rebuildFtsTagsForNote)** — Sonnet implementer + 회귀 test ✅. importNote gap 명시 보고 (final review 발견 사전 알림) - **Tasks 3-5 (ftsHelpers + search + reviewAggregate)** — Sonnet batch implementer ✅, 18 신규 test - **Task 6 (types + IPC + preload)** — Sonnet implementer ✅, 3 IPC test (period validation 포함) - **Task 7 (store)** — Sonnet implementer ✅, view enum + 4 actions + 3 test - **Task 8 (SearchBox + 헤더)** — Sonnet implementer ✅, hooks-of-rules 보강 (subscribe 위치 정정) - **Task 9 (ReviewView)** — Sonnet implementer ✅, NoteCard 의 inboxApi mock 자동 보강 - **Final code review (Opus, 1차)** — ⚠ Approved with follow-up (importNote gap) - **Final fix re-verification** — ✅ 608/608 + typecheck 0 ### 머지 권장 1. PR #29 머지 2. tag `v0.2.11` + `npm run dist:win` → Windows exe 빌드 3. Gitea release v0.2.11 + exe attach 4. macOS host: `dist:mac` + `dist:linux` 후 dmg/AppImage/deb 추가 attach 5. 메모리 정책 갱신 — search source = latest (revision X) + single write path 강제 정책 보강 (3 path 검증) 6. dogfood 1주 soak — search 사용 빈도 + 한국어 token 정확도 + 회고 view 사용 빈도 → v0.2.12 또는 Cut E 진입 결정 ### Overall **Ready to merge.** Spec 100% coverage, 569 → 608 unit (+39) + typecheck 0, m007 마이그레이션 외래키 안전 + trigger 자동 sync, single write path 정책 invariant 검증 (3 path 전수 점검), v0.2.6 #10 fix / Cut B 'disabled' / Cut C revision API 모두 호환. final review follow-up 완료. Cut E (v0.3.0 — F21 양방향 sync) 진입 가능. 🤖 Reviewed by Claude Opus 4.7 (1M context) with Sonnet/Haiku sub-agents per subagent-driven-development skill
altair823 merged commit 2e9a82face into main 2026-05-09 15:52:19 +00:00
altair823 deleted branch worktree-v0211-cut-d-fts5-review 2026-05-09 15:52:21 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: altair823-org/inkling#29