docs/components/<group>/README.md 12 페이지 + 인덱스 작성. 각 그룹 페이지가 구성 crate 표 + 구조 mermaid + data flow mermaid + 주요 type/trait/함수 시그니처 + 외부 의존 + 핵심 결정 (HOTFIXES + spec 의 "왜" 통합) + 관련 spec/HOTFIXES 링크. 인덱스가 그룹 wiring 다이어그램 + 진입 가이드 보유. ARCHITECTURE.md 의 ASCII crate 의존 그래프를 mermaid flowchart 로 교체 (등가 정보, Gitea/GitHub 자동 렌더). docs/components/ 진입 링크 추가. 이 layer 는 contributor 향 — 사용자 향 grand picture 는 README.md 의 logical-architecture diagram 그대로 유지. 진척도는 HANDOFF.md, per-task spec 은 tasks/INDEX.md 가 기존대로 source of truth. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Source
워크스페이스를 walk 하고 gitignore-style 필터로 거른 뒤 BLAKE3 checksum 으로 컨텐츠 어드레스된
RawAsset목록을 만든다. ingest pipeline 의 첫 단계.
구성 crate
| Crate | 역할 |
|---|---|
kebab-source-fs |
로컬 파일시스템 SourceConnector 단일 구현. kebab-config + kebab-core 만 의존. |
구조
classDiagram
class FsSourceConnector {
+new(config) Result~Self~
+scan(scope) Result~Vec~RawAsset~~
-default_root: PathBuf
-default_exclude: Vec~String~
-copy_threshold_bytes: u64
}
class WalkerModule {
build_overrides(root, excludes, kbignore) Override
read_kbignore(root) Vec~String~
walk_files(root, overrides) Vec~PathBuf~
}
class HashModule {
hash_file(path) (byte_len, hex)
}
class MediaModule {
media_type_for(path) MediaType
}
class SourceConnector {
<<trait kebab-core>>
scan(scope) Vec~RawAsset~
}
SourceConnector <|.. FsSourceConnector
FsSourceConnector --> WalkerModule
FsSourceConnector --> HashModule
FsSourceConnector --> MediaModule
Data flow
flowchart LR
Cfg["Config<br/>(workspace.root, exclude, copy_threshold_mb)"]
Scope["SourceScope<br/>(per-call lens)"]
Walk["walk_files<br/>walkdir + gitignore overrides<br/>+ symlink cycle guard"]
KBI[".kebabignore<br/>(매 scan 재읽음)"]
Default["DEFAULT_EXCLUDES<br/>.DS_Store / ._*"]
Hash["blake3 file hash<br/>(byte_len + hex)"]
Media["media_type_for<br/>(extension → MediaType)"]
Posix["to_posix<br/>NFC + leading ./ strip<br/># 거부"]
Asset["RawAsset<br/>{asset_id, workspace_path,<br/>media_type, byte_len,<br/>checksum, stored}"]
Sort["sort by workspace_path"]
Cfg --> Walk
Scope --> Walk
KBI --> Walk
Default --> Walk
Walk --> Hash --> Asset
Walk --> Media --> Asset
Walk --> Posix --> Asset
Asset --> Sort --> Out["Vec~RawAsset~"]
주요 type / trait / 함수
FsSourceConnector::new(&Config) -> Result<Self>—Config::resolve_workspace_root()호출 (p9-fb-05 path policy 통일),copy_threshold_mb * 1 MiB미리 곱해 둠.FsSourceConnector::scan(&SourceScope) -> Result<Vec<RawAsset>>— kebab-coreSourceConnectortrait 구현. 결정성 보장 sort byworkspace_path.walker::build_overrides(root, config_exclude, kbignore_patterns) -> Override—ignorecrate 의 gitignore engine 위에서 union 빌드. 모든 패턴이!prefix (positive override = include 의미라 negate 필요).walker::read_kbignore(root) -> Vec<String>—<root>/.kebabignore매 scan 재읽음 (long-running process 가 file edit 즉시 반영).walker::walk_files(root, overrides) -> Vec<PathBuf>—walkdir::WalkDir::follow_links(true)+ 별도visited: HashSet<canonical PathBuf>으로 symlink cycle 방어.hash::hash_file(path) -> Result<(byte_len, hex)>— streaming BLAKE3, 큰 파일도 메모리 안 쌓음.media::media_type_for(path) -> MediaType— extension 기반 단순 매핑 (.md→Markdown,.pdf→Pdf,.png/.jpg/...→Image(_)등).
외부 의존
- crate dep:
kebab-core(RawAsset,Checksum,SourceConnector,id_for_asset,to_posix),kebab-config(Config). - 외부 lib:
walkdir(디렉토리 walk + symlink follow),ignore(gitignore override engine, walker 는 안 씀),blake3(file hash),time(OffsetDateTime::now_utcfordiscovered_at). - 외부 서비스: 없음.
핵심 결정
-
walkdir사용 (notignore::WalkBuilder). 왜:ignore::WalkBuilder가 gitignore + cycle detection 을 한 번에 묶지만, sibling-subtree symlink (a -> ../b) 의 cycle 을 ancestor-only check 가 놓치는 케이스가 있음.walkdir+ 자체visitedset 으로 canonical-path 비교를 명시 제어. -
DEFAULT_EXCLUDES =
.DS_Store+._*. 왜: macOS Finder 메타파일 + AppleDouble resource fork. 모든 사용자.kebabignore에 들어갈 noise — 한 번에 baked-in. 사용자가 끄려면 별 메커니즘 필요 (현재 미제공, 필요 시 P+). -
AssetStorage::{Copied, Reference}= intent signal, not actual copy. 왜: scan 단계는 byte_len 만 보고 의도만 표시. 실제 디스크 copy 는 P1-6 의 asset writer 책임.copy_threshold_mb = 100(default) 미만은Copied, 이상은Reference + sha. 큰 파일 중복 저장 회피. -
SourceScope::include무시 (router 책임). 왜: §6.2 의WorkspaceCfg.include는 extractor router 가 적용. SourceConnector 가 또 필터링하면 markdown/PDF 가 router 에 도달 전 이중 필터. Connector 는 모든 non-excluded 파일 emit. -
Filename
#거부 = warn + skip (not abort). 왜:to_posix가#포함 path 를 Err 반환 (Citation URI fragment separator 와 충돌). 단일 파일이 10000 파일 ingest 를 죽이면 안 됨 —tracing::warn로 알리고 다음 파일. -
scan 결과 sort by
workspace_path. 왜: 결정성. 두 번 scan 의Vec<RawAsset>가discovered_at빼고 동일해야 idempotent ingest 가능. testscan_idempotent_modulo_timestamp가 회귀 핀.
관련 spec / HOTFIXES
- frozen 설계 §3.3 (
RawAsset), §6.2 (workspace + .kebabignore), §6.6 (POSIX 정규화), §7.1 (SourceScope), §7.2 (SourceConnector):docs/superpowers/specs/2026-04-27-kebab-final-form-design.md - task spec:
tasks/p1/p1-1-source-fs.md - p9-fb-05 (
expand_tildeshim 제거 +Config::resolve_workspace_root일원화):tasks/p9/p9-fb-05-config-path-policy.md