Skip to content

feat(docs): docs/plans 가상 필터 + index 자동 생성 + #300 shallow copy (Phase 1)#301

Merged
hang-in merged 4 commits into
mainfrom
feat/docs-plans-filter-and-index
May 29, 2026
Merged

feat(docs): docs/plans 가상 필터 + index 자동 생성 + #300 shallow copy (Phase 1)#301
hang-in merged 4 commits into
mainfrom
feat/docs-plans-filter-and-index

Conversation

@hang-in
Copy link
Copy Markdown
Owner

@hang-in hang-in commented May 29, 2026

docs/plans 정리 Phase 1 — 가상 필터 + index 자동 + Gemini #300 shallow copy

  • Plan: docs/plans/docsPlansOrganizationPlan_2026-05-29.md (§3 Phase 1 = T1~T5)
  • 핸드오프: docs/prompts/docsPlansOrganizationDeveloperHandoff_2026-05-29.md
  • 정책 정합: documentationNavigationModel_2026-03-30.md ("메타+인덱스로 navigation"), INV-DPO-1~6
  • 경로 불변 — 월폴더 신설 X, 완료 plan 파일 이동 X (Phase 2 별 PR)

Task 별 변경

Task 변경
T1+T2 DocsSection 에 DB plan 메타 join (list_plans_by_project) + status 배지 + status/날짜 가상 필터. docs/plans/{slug}.md 본문만 배지, 동반 파일·DB 미등록 doc·디렉터리 graceful. i18n ko/en. (단일 파일 재작성 단위라 T1/T2 합본 커밋)
T3 regenerate_plans_index Rust command — DB plans 기준 active 테이블 + archive 요약. 마커 (<!-- AUTO-INDEX-START/END -->) 기반 부분 갱신, 수동 영역 보존. index.md 마커 seed.
T4 branchSlice.ts shadow conv engine 상속 { ...parentEngine } shallow copy (Gemini PR #300 review medium). 상속 동작 보존 + mutation 격리.
T5 순수 로직을 planDocsFilter.ts 로 추출 + vitest 17건 (slug join / graceful / 날짜 버킷 / 트리 필터 / shallow copy 격리).

필터 source = DB plans (frontmatter 파싱 X)

status 필터 옵션은 DB PlanStatus enum (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 --lib661 passed (single-thread). +5 T3 unit test. 병렬 실행 시 audit_enabled_respects_off_values 1건 간헐 실패 — 본 PR 무관한 기존 env-var-mutation race (TUNAFLOW_AGENT_SESSION_AUDIT, agent_session_tx.rs, 내 diff 비대상). 단일 스레드 0 fail 로 확인.
  • tsc --noEmit — clean
  • vitest run513 passed (50 files). baseline 496 +17.

회귀 가드 grep (모두 통과)

  • ContextPack loader (agents_helpers/) 변경 0 파일
  • 월폴더 경로 (docs/plans/YYYY-MM/) 0 매치
  • branchSlice 변경 = { ...parentEngine } shallow copy 1줄 + 주석만
  • main repo 워킹트리 비오염 (worktree 격리)

e2e (자동 / 사용자 영역)

  • 자동: T1 slug join / graceful / 동반 평면 / 날짜 버킷 / status·date 트리 필터 / T4 mutation 격리 — vitest 커버
  • 사용자 영역 (GUI): DocsSection 배지·필터 칩 실시 표시, 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

dghong and others added 4 commits May 29, 2026 18:16
…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>
@hang-in hang-in merged commit 099a6b8 into main May 29, 2026
2 checks passed
@hang-in hang-in deleted the feat/docs-plans-filter-and-index branch May 29, 2026 09:26
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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('|', "\\|");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

플랜 제목(p.title)에 대괄호([ 또는 ])가 포함되어 있을 경우, 생성되는 마크다운 링크([제목](./경로.md))의 문법이 깨질 수 있습니다. 대괄호도 함께 이스케이프 처리해 주는 것이 안전합니다.

Suggested change
let title = p.title.replace('|', "\\|");
let title = p.title.replace('|', "\\|").replace('[', "\\[").replace(']', "\\]");

Comment on lines +77 to +82
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 };
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

필터링 적용 후 자식 요소가 모두 필터링되어 빈 디렉터리가 된 경우, 해당 디렉터리 자체를 화면에서 숨기는 것이 UI상 더 깔끔합니다. 원래부터 비어있던 디렉터리는 유지하되, 필터링으로 인해 비어있게 된 디렉터리는 null을 반환하여 숨기도록 개선할 수 있습니다.

Suggested change
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 };
}

Comment on lines +160 to +170
useEffect(() => {
if (!projectKey) {
setPlans([]);
return;
}
let alive = true;
loadPlanMeta(projectKey).then((rows) => {
if (alive) setPlans(rows);
});
return () => { alive = false; };
}, [projectKey]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

현재 DB plan 메타데이터(plans)는 projectKey가 변경될 때만 최초 1회 로드됩니다. 이로 인해 사용자가 플랜의 상태를 변경하거나 새 플랜을 생성하더라도 사이드바의 상태 배지와 필터가 실시간으로 업데이트되지 않고 이전 상태로 유지되는(stale) 문제가 발생할 수 있습니다.

scanDocs가 실행될 때(즉, 문서 목록이 갱신될 때) plans 메타데이터도 함께 다시 로드하도록 두 useEffect를 통합하거나, 플랜 상태 변경 시 이를 트리거할 수 있는 메커니즘을 추가하는 것을 권장합니다.

hang-in added a commit that referenced this pull request May 29, 2026
…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>
hang-in pushed a commit that referenced this pull request May 29, 2026
매니페스트 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant