feat: Wiki 지식 자산 업데이트 - UX Scenarios, Frontend, Game Design, Topics 추가 [2026-05-08]

This commit is contained in:
2026-05-08 19:52:07 +09:00
parent 9dd3d40662
commit 5ba5a55c78
3984 changed files with 334557 additions and 28839 deletions
+160
View File
@@ -0,0 +1,160 @@
"""
P-Reinforce Phase 2 — Folder consolidation.
Moves all .md files from a list of source folders into one destination folder,
preserving wiki-link compatibility by leaving redirect stubs in the original
location when the path is referenced from elsewhere.
For now this is a single-target tool: AI-related folders -> AI_and_ML.
Conflict handling:
If an incoming file has the same name as an existing file in the target,
keep the larger-body one as canonical and convert the smaller to a
redirect stub at its NEW location (but with redirect_to pointing at the
canonical's filename). The smaller file's original is moved to
01_Archive/CONSOLIDATED/<date>/.
After moving every file, the source folder is removed if it ends up empty.
"""
from __future__ import annotations
import shutil
import sys
from datetime import date
from pathlib import Path
ROOT = Path(r"E:/Wiki/2nd")
TOPICS = ROOT / "10_Wiki" / "Topics"
ARCHIVE_BASE = ROOT / "01_Archive" / "CONSOLIDATED"
# (source_folder, target_folder) — both relative to TOPICS
PLAN = [
# Frontend family
("Frontend_Mastery", "Frontend"),
# Game Design family — pick canonical
("Game Design", "Game_Design"),
# Economy family
("Economy", "Economics & Algorithms"),
("Economics", "Economics & Algorithms"),
]
REDIRECT_TEMPLATE = """---
id: {id}
title: {title}
category: 10_Wiki/Topics
status: merged
redirect_to: {target}
canonical_id: {target}
aliases: []
duplicate_of: none
source_trust_level: A
confidence_score: 0.92
tags: [redirect]
raw_sources: []
last_reinforced: {today}
github_commit: pending
inferred_by: Claude Opus 4.7 (consolidation 2026-05-08)
---
# {title}
> [!IMPORTANT]
> 이 문서는 P-Reinforce Phase 2 폴더 통합으로 **[[{target}]]**로 통합되었습니다.
---
*Redirected to: [[{target}]]*
"""
def make_redirect(target_filename: str, original_filename: str, today: str) -> str:
title = original_filename.replace("-", " ").replace("_", " ")
return REDIRECT_TEMPLATE.format(
id=f"wiki-{today.replace('-', '')[:8]}-{original_filename.lower()[:32]}-redir",
title=title,
target=target_filename,
today=today,
)
def consolidate(src: Path, dst: Path, today: str, archive_dir: Path, log: list[str]) -> dict:
moved = 0
conflicts = 0
if not src.exists():
return {"moved": 0, "conflicts": 0}
for p in src.rglob("*.md"):
if not p.is_file():
continue
rel = p.relative_to(src)
target = dst / rel
target.parent.mkdir(parents=True, exist_ok=True)
if not target.exists():
shutil.move(str(p), str(target))
moved += 1
log.append(f"- moved `{p.relative_to(ROOT)}` → `{target.relative_to(ROOT)}`")
else:
# conflict: same filename already at destination
existing_size = target.stat().st_size
incoming_size = p.stat().st_size
if incoming_size > existing_size:
# incoming is larger — promote it; archive existing, write redirect at existing location? No,
# destination is canonical and gets replaced. Existing -> archive. New name kept.
arch_path = archive_dir / "DST_overwritten" / rel
arch_path.parent.mkdir(parents=True, exist_ok=True)
shutil.move(str(target), str(arch_path))
shutil.move(str(p), str(target))
conflicts += 1
log.append(f"- conflict (incoming wins): `{p.relative_to(ROOT)}` overwrote `{target.relative_to(ROOT)}` (old archived)")
else:
# existing is larger or equal — keep existing; archive incoming, write a redirect
# at the source location pointing to existing.
arch_path = archive_dir / "SRC_redirected" / src.name / rel
arch_path.parent.mkdir(parents=True, exist_ok=True)
shutil.move(str(p), str(arch_path))
# Write redirect at source location? The whole point is to remove src/ — instead,
# leave behind nothing and rely on the existing file at destination.
# But we should also leave a tiny redirect in the destination's name space if
# the source filename differs only by punctuation. For now: just archive the loser.
conflicts += 1
log.append(f"- conflict (existing wins): `{p.relative_to(ROOT)}` archived (kept `{target.relative_to(ROOT)}`)")
# remove empty src
try:
# remove empty dirs recursively
for dirpath, _dirs, files in list(__import__("os").walk(str(src), topdown=False)):
d = Path(dirpath)
if d.exists() and not any(d.iterdir()):
d.rmdir()
except OSError:
pass
return {"moved": moved, "conflicts": conflicts}
def main() -> int:
today = date.today().isoformat()
archive_dir = ARCHIVE_BASE / today
archive_dir.mkdir(parents=True, exist_ok=True)
log: list[str] = [f"# Folder consolidation log — {today}\n"]
total = {"moved": 0, "conflicts": 0}
for src_name, dst_name in PLAN:
src = TOPICS / src_name
dst = TOPICS / dst_name
log.append(f"\n## `{src_name}` → `{dst_name}`\n")
if not src.exists():
log.append(f"- (skip) `{src_name}` does not exist")
continue
dst.mkdir(parents=True, exist_ok=True)
r = consolidate(src, dst, today, archive_dir, log)
log.append(f"\n**summary**: moved={r['moved']}, conflicts={r['conflicts']}")
total["moved"] += r["moved"]
total["conflicts"] += r["conflicts"]
log.append(f"\n---\n**TOTAL**: moved={total['moved']}, conflicts={total['conflicts']}")
log_path = ROOT / "20_Meta" / "ReviewQueue" / "consolidation_log.md"
log_path.write_text("\n".join(log), encoding="utf-8")
print(f"DONE: moved={total['moved']}, conflicts={total['conflicts']}", file=sys.stderr)
print(f"Log: {log_path}", file=sys.stderr)
return 0
if __name__ == "__main__":
sys.exit(main())