feat(docs): docs/plans 가상 필터 + index 자동 생성 + #300 shallow copy (Phase 1)#301
Conversation
…s section (T1+T2)
docs/plans 항목에 DB `plans` 테이블 (`list_plans_by_project`) 메타 join —
`docs/plans/{slug}.md` 파일명 slug → plan lookup → status 배지 표시.
status (all/draft/active/done/abandoned) + 날짜 (all/7d/30d/이번달, updated_at)
가상 필터 추가 (frontend state, 경로 불변). DB 미등록 doc·동반 파일·디렉터리는
graceful (배지 없음 + 필터 무관 항상 표시, INV-DPO-1). i18n ko/en.
T1(join/배지)·T2(필터 UI)는 같은 DocsSection 재작성 단위라 합본 커밋
(T2 의 slugToPlan 의존 + 단일 파일 hunk 분리 비현실적). 회귀 0 — 필터 미사용
시 원본 entries 그대로.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`regenerate_plans_index` command 추가 — DB `plans` 테이블 (project_key join) 기준으로 docs/plans/index.md 의 자동 영역만 갱신. 마커 (`<!-- AUTO-INDEX-START/END -->`) 기반 부분 갱신으로 수동 설명 영역 (navigationModel 구조 안내) 보존 (INV-DPO-4). 마커 없으면 append, 있으면 교체. active 테이블 (title/status/updated_at/slug link, updated_at DESC) + archive 요약 (done/abandoned 카운트). 경로 불변 — 파일 이동 없음. 순수 helper (build_plans_index_block / merge_index_block) 분리 + 5 unit test. docs/plans/index.md 에 마커 seed (수동 통계/설명 보존). frontend API binding (regeneratePlansIndex) 추가. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shadow conv 가 parent conv 의 engine state 를 상속할 때 객체 참조를 공유하면 이후 parent mutation 이 branch 로 새는 위험 (Gemini PR #300 review medium). `saveConversationEngine(branchConvId, parentEngine)` → `{ ...parentEngine }` shallow copy. ConversationEngineState 는 1-depth primitive 필드라 shallow 로 완전 격리. PR #300 상속 동작 자체는 보존. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… extract (T5) T1/T2 순수 로직 (slug join / companion 판별 / 날짜 버킷 / 트리 필터) 을 planDocsFilter.ts 로 추출 (DocsSection 은 import 만) → 단위 테스트 가능화. - docsPlansFilter.test.ts: slug 매칭 / DB 미등록 graceful / 동반 파일 평면 / windows 경로 / 7d·30d·month 경계 / status·date 트리 필터 (회귀 0 가드 포함). - branchEngineShallowCopy.test.ts: T4 상속 보존 + parent mutation 독립 격리 (별 참조) + re-entry 보존 (INV-BAF-2). vitest 496 → 513 (+17). DocsSection 동작 불변 (helper 추출 리팩토링만). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces an automated plan index generation feature and metadata-based filtering for plan documents in the sidebar. Key changes include backend commands to partially update docs/plans/index.md using markers, frontend components to display status badges and filter documents by status and date, and a fix to shallow copy inherited engine states in branchSlice.ts to prevent state leakage. The reviewer provided valuable feedback on escaping brackets in plan titles to avoid broken markdown links, hiding directories that become empty after filtering, and resolving a potential stale state issue by reloading plan metadata more dynamically.
| for p in &active { | ||
| let slug = p.slug.clone().unwrap_or_else(|| slugify(&p.title)); | ||
| let date = fmt_index_date(p.updated_at); | ||
| let title = p.title.replace('|', "\\|"); |
| if (entry.isDir) { | ||
| const children = (entry.children ?? []) | ||
| .map((c) => filterDocEntry(c, slugToPlan, statusFilter, dateFilter, now)) | ||
| .filter((c): c is DocEntry => c !== null); | ||
| return { ...entry, children }; | ||
| } |
There was a problem hiding this comment.
필터링 적용 후 자식 요소가 모두 필터링되어 빈 디렉터리가 된 경우, 해당 디렉터리 자체를 화면에서 숨기는 것이 UI상 더 깔끔합니다. 원래부터 비어있던 디렉터리는 유지하되, 필터링으로 인해 비어있게 된 디렉터리는 null을 반환하여 숨기도록 개선할 수 있습니다.
| if (entry.isDir) { | |
| const children = (entry.children ?? []) | |
| .map((c) => filterDocEntry(c, slugToPlan, statusFilter, dateFilter, now)) | |
| .filter((c): c is DocEntry => c !== null); | |
| return { ...entry, children }; | |
| } | |
| if (entry.isDir) { | |
| const children = (entry.children ?? []) | |
| .map((c) => filterDocEntry(c, slugToPlan, statusFilter, dateFilter, now)) | |
| .filter((c): c is DocEntry => c !== null); | |
| if ((entry.children ?? []).length > 0 && children.length === 0) { | |
| return null; | |
| } | |
| return { ...entry, children }; | |
| } |
| useEffect(() => { | ||
| if (!projectKey) { | ||
| setPlans([]); | ||
| return; | ||
| } | ||
| let alive = true; | ||
| loadPlanMeta(projectKey).then((rows) => { | ||
| if (alive) setPlans(rows); | ||
| }); | ||
| return () => { alive = false; }; | ||
| }, [projectKey]); |
There was a problem hiding this comment.
…r + stale meta) (#302) * fix(plans): escape brackets in index title markdown link (gemini PR #301, T1) index.md 자동 생성 시 plan title 의 `[`/`]` 가 markdown link `[title](path)` 의 링크 텍스트를 깨뜨리는 문제. 기존 `|` escape 에 `[`/`]` 추가. 회귀 테스트 추가. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(docs-panel): hide directories emptied by filter (gemini PR #301, T2) status/날짜 필터로 자식이 전부 걸러진 디렉터리를 null 반환해 숨긴다. 원래부터 비어 있던 dir 은 그대로 유지 (필터로 빈 것만 숨김). 회귀 테스트 3건 추가. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(docs-panel): reload plan metadata on docs refresh to avoid stale badges (gemini PR #301, T3) 이전엔 plan 메타가 projectKey 변경 시 1회만 로드 → plan status 변경·새 plan 생성이 사이드바 배지·필터에 미반영 (stale). window focus / visibilitychange 복귀 시 refreshTick 증가 → docs 스캔 + plan 메타 동시 재로드. 타이머 polling 없이 사용자가 창으로 돌아오는 자연스러운 시점에만 재fetch (과도한 re-fetch 회피). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: dghong <d9ng@outlook.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
매니페스트 4 곳 + Cargo.lock bump. CHANGELOG entry 추가. 핵심 (PR #301 + #302): - docs/plans 가상 필터 (status/날짜) + 상태 배지 (DB plan join, 경로 불변) - regenerate_plans_index command (마커 기반 부분 갱신) - stale 배지 / index 링크 escape / 빈 dir / shallow copy fix 다모앙 커뮤니티 사용자 요청. Phase 2 (archive 물리 이동) 는 검증 후 별도. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
docs/plans 정리 Phase 1 — 가상 필터 + index 자동 + Gemini #300 shallow copy
docs/plans/docsPlansOrganizationPlan_2026-05-29.md(§3 Phase 1 = T1~T5)docs/prompts/docsPlansOrganizationDeveloperHandoff_2026-05-29.mddocumentationNavigationModel_2026-03-30.md("메타+인덱스로 navigation"), INV-DPO-1~6Task 별 변경
DocsSection에 DB plan 메타 join (list_plans_by_project) + status 배지 + status/날짜 가상 필터.docs/plans/{slug}.md본문만 배지, 동반 파일·DB 미등록 doc·디렉터리 graceful. i18n ko/en. (단일 파일 재작성 단위라 T1/T2 합본 커밋)regenerate_plans_indexRust command — DB plans 기준 active 테이블 + archive 요약. 마커 (<!-- AUTO-INDEX-START/END -->) 기반 부분 갱신, 수동 영역 보존.index.md마커 seed.branchSlice.tsshadow conv engine 상속{ ...parentEngine }shallow copy (Gemini PR #300 review medium). 상속 동작 보존 + mutation 격리.planDocsFilter.ts로 추출 + vitest 17건 (slug join / graceful / 날짜 버킷 / 트리 필터 / shallow copy 격리).필터 source = DB plans (frontmatter 파싱 X)
status 필터 옵션은 DB
PlanStatusenum (draft/active/done/abandoned) 에 정합 — 핸드오프의 "in_progress/archived" 표기는 실제 DB enum 으로 매핑 (active/abandoned). 옛 plan> Status:blockquote 형식 불일치 회피 (INV-DPO-1).Verification (모두 PASS)
cargo check— Finished (clean)cargo test --lib— 661 passed (single-thread). +5 T3 unit test. 병렬 실행 시audit_enabled_respects_off_values1건 간헐 실패 — 본 PR 무관한 기존 env-var-mutation race (TUNAFLOW_AGENT_SESSION_AUDIT, agent_session_tx.rs, 내 diff 비대상). 단일 스레드 0 fail 로 확인.tsc --noEmit— cleanvitest run— 513 passed (50 files). baseline 496 +17.회귀 가드 grep (모두 통과)
agents_helpers/) 변경 0 파일docs/plans/YYYY-MM/) 0 매치branchSlice변경 ={ ...parentEngine }shallow copy 1줄 + 주석만e2e (자동 / 사용자 영역)
regenerate_plans_index호출 후 index.md 자동 영역 갱신T1 동반 파일 UX
본문
{slug}.md만 배지 (DB plan join). 동반 파일 (-task-NN/-review-rN/-result) 은 평면 표시 (배지 없음, 필터 무관 항상 표시) — Phase 1 결정 (handoff DO #2). nest 그룹핑은 Phase 2.T3 index.md 마커 구조
<!-- AUTO-INDEX-START -->~<!-- AUTO-INDEX-END -->사이만 자동 (active 테이블 + archive 요약). 마커 밖 수동 설명/통계 보존. 마커 부재 시 끝에 append, 존재 시 교체.🤖 Generated with Claude Code