refactor(parse-md): absorb kebab-normalize + kebab-parse-types — 24 → 22 crates + §3.7b 재작성 #186
Reference in New Issue
Block a user
Delete Branch "refactor/normalize-absorption"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
요약
kebab-normalize(1097 LOC, build_canonical_document + derive_title) +kebab-parse-types(98 LOC, 5 사용 type + 3 forward-declared struct) 두 crate 를kebab-parse-md로 흡수. 24 → 22 crates + frozen design §3.7b 4-단락 재작성 + §8 graph 갱신 + HOTFIXES design deviation entry.medium-agnostic lift layer (
kebab-normalize) 가 markdown 만 통과하는 reality 와 design §3.7b 의 intent (4 parser 통합 lift) 갭 해소 — 4 parser 중 md 만 lift 경유, 3 parser (pdf/image/code) 가CanonicalDocument직접 emit 으로 lift bypass. 흡수 후kebab-parse-md가 multi-parser lift 의 single destination — future image/pdf normalize integration (v0.20+) 의 base.설계: docs/superpowers/specs/2026-05-26-normalize-absorption-spec.md (3 round APPROVE)
계획: docs/superpowers/plans/2026-05-26-normalize-absorption-plan.md (3 round ACCEPT)
핵심 변경
kebab-parse-md::types신설 (98 LOC) — 5 사용 type (ParsedBlock/ParsedBlockKind/ParsedPayload/Warning/WarningKind) + 3 forward-declared struct (ParsedImageRegion/ParsedPdfPage/ParsedAudioSegment) 모두 byte-identical 이식. 3 dead struct 는 보존 (future re-extraction trigger 대비 — spec §11).kebab-parse-md::normalize신설 (1097 LOC) —build_canonical_document+derive_title+warning_agent+ 6 hard-coded literal (tracing targettarget: "kebab-normalize"+ 5 agent string"kb-source-fs"/"kb-parse-md"/"kb-normalize") 모두 byte-identical 이식 (stage label 일관성 — SQLite audit log + tracing log grep 호환).kebab-parse-md/Cargo.toml—kebab-parse-typesregular dep 제거 +unicode-normalization = "0.1"추가 (NFKC 의존성 흡수) + description 갱신.lib.rs:51import path +:1119context string.:1308+:1474historical comment 는 보존 (git blame 일관성 + reader 가 흡수 history 추적 가능).use갱신.parse-md → parse-types,normalize → parse-types,kebab-normalize entry) + 2 forbidden bullet 의미 갱신 + commentary 한 줄 (기존parse-* → store/llm/embed ✗룰이 흡수된 lift 까지 자동 포함).workspace.members24 → 22 +workspace.package.version0.18.0 → 0.19.0 (frozen design contract 변경 trigger — CLAUDE.md "Release / binary version bump" 룰).tasks/INDEX.md"Future work / deferred" 섹션 신설 + L169 v0.18.1+ defer mention closure + image/pdf normalize integration entry.OMC review history
Phase A (spec, 3 round):
asset: &RawAssetvs spec&AssetInfo,metadata: Metadataby-value vs spec&Metadata,derive_title의&[Block]vs spec&[ParsedBlock]) / CRITICAL #2 dev-dep direction 부정확 / CRITICAL #3 §11 미존재 (false-positive, line 793 실존 verified)Phase B (plan, 3 round):
unicode-normalization신규 dep 누락 /pub use kebab_core::{id_for_*}제거 시 in-body unqualified call 깨짐 / parse-md tests/ stale import / blocks.rs 의 4 hit file-wide replace 누락 / Step 7 verify self-contradictionImplementation (executor):
710945c)검증
11 acceptance gate (plan §4 + Step 15 exit gate)
cargo test -p kebab-parse-md -j 1→ green (workspace test 안 통과)cargo test -p kebab-chunk/store-sqlite -j 1→ greencargo build -p kebab-app -j 1+cargo tree -p kebab-app --depth 2 | grep -E "kebab_(parse_types|normalize)" | wc -l→ 0 줄cargo metadata --no-deps | jq '.workspace_members | length'→ 22cargo test --workspace --no-fail-fast -j 1→ 1313 PASS, 0 failed (spec §5.1 expected baseline 과 byte-identical match, net delta = 0)cargo clippy --workspace --all-targets -j 1 -- -D warnings→ clean (5m 46s, 0 warning)cargo build --release -p kebab-cli -j 1→ clean (36m 20s, 0 warning, binary 262M, kebab-cli v0.19.0)cargo deny check→ CI 검증 (본 머신 미설치)Literal preservation verify
grep -c 'target: "kebab-normalize"' crates/kebab-parse-md/src/normalize.rs→ 1 (tracing target 보존)grep -cE 'agent:\s*"kb-(source-fs|parse-md|normalize)"\.to_string\(\)' crates/kebab-parse-md/src/normalize.rs→ 4 (4 hard-coded literal 보존)grep -c "kebab_parse_types::" crates/kebab-parse-md/src/normalize.rs→ 0 (9 hit fully-qualified path 갈음 후)Source migration verify
grep -rn "kebab_parse_types" crates/kebab-parse-md/production hit → 0 (doc-comment 의 historical mention 3 hit 만 — production code 0)grep -rn "kebab_normalize\|kebab_parse_types" crates/kebab-app/src/→ 0 (line 51/1119 갱신, 1308/1474 comment 보존)Doc + frozen contract verify
grep -c "design deviation — post-PR9 audit" tasks/HOTFIXES.md→ 1grep -c "## Future work / deferred" tasks/INDEX.md→ 1git diff main..HEAD --name-only | grep "^tasks/p"→ 0 (~25 referencing task spec frozen 유지)git diff main..HEAD -- docs/wire-schema/v1/→ 0 (wire schema 변경 0)Cargo.lock + version cascade
Cargo.lock의kebab-normalize+kebab-parse-types[[package]]entry → 0 (자동 cleanup)v0.19.0cascade 적용 (workspace.package version inherit)Wire / 변경 없음
docs/wire-schema/v1/*.json): 변경 0warning_agent("kb-...")return string: 보존 (SQLitedocuments.provenance_jsonBLOB persist, wire-invisible — spec §1.9 의 16-row production flow trace 검증)tracing::debug!(target: "kebab-normalize", ...)literal: 보존 (log scraper grep 일관성)tasks/p<N>/): frozen 유지 — CLAUDE.md "Task specs themselves stay frozen as the historical contract" + HOTFIXES.md live source 룰parser_versioncascade: 변경 0workspace.package.version0.18.0 → 0.19.0 (minor bump) 가 internal Rust crate-API surface 의 이동에 따른 design contract 변경 trigger — 사용자 docpfood 영향 0 이지만 CLAUDE.md "Release / binary version bump" 의 "frozen design contract 변경 머지 후" trigger 충족.시험 항목 (Test Plan)
cargo test --workspace --no-fail-fast -j 1→ 1313 PASS 그대로 (net delta = 0)cargo metadata --no-deps | jq '.workspace_members | length'→ 22cargo build --release -p kebab-cli -j 1→ green (binary 262M, v0.19.0)cargo clippy --workspace --all-targets -j 1 -- -D warnings→ cleangrep -c "kebab_parse_types::" crates/kebab-parse-md/src/→ 0Assisted-by: Claude Code
회차 1 — normalize + parse-types absorption refactor 검토.
OMC team
normalize-absorption의 3-round spec APPROVE (analyst → planner spec drafter → 3 critic round) + 3-round plan ACCEPT (planner → 3 round critic-plan + 2 round verifier-plan) 의 모든 closure 결과가 본 PR 의 코드에 정확히 reflect. workspace 1313 tests + 11 acceptance gate 모두 green.칭찬 (산문):
design intent 자체 보존하는 §3.7b 4-단락 재작성 — spec round 1 critic 의 핵심 질문 ("§3.7b 의 raison d'être 가 dead 인데 strike 가 정답?") 에 대한 답이 strike 아닌 4-단락 재작성 (original intent + reality + 보존된 surface + future re-extraction trigger). 사용자 결정 (dead struct 3 보존 — future surface 명시) + multi-region image / multi-block pdf 의 future trigger 가능성이 design contract 의 raison d'être 와 align. ~25 referencing task spec 의 historical contract 인용도 의미적 backward-compat.
signature byte-identical 보존의 micro-precision — spec round 1 CRITICAL #1 의 발견 (
asset: &RawAssetvs spec 초안&AssetInfo,metadata: Metadataby-value vs&Metadata,derive_title의&[Block]lifted vs&[ParsedBlock]pre-lift) 가 plan + executor 단계까지 정확히 propagate. 특히derive_title의&[Block](lift 이후 호출) 의 의미적 구분이 보존되어 caller 가 type-error 회피.unicode-normalizationdep silent drop 회피 — plan round 1 critic CRITICAL #1 + verifier GAP2 둘 다 잡은 결함 (spec §3.4 의 hunk 가 plan 의 어느 step 에도 명시 안 됨). round 2 reflection 으로 Step 3 Action (e) 신설 + Cargo.toml hunk + exit gategrep -c "unicode-normalization" ≥ 1모두 closure. executor 단계에서 actual lib.rs:31 의 use 가 normalize.rs 동반 이식 시 unresolved crate error 회피.pub use kebab_core::{id_for_*}제거의 in-body call resolve 정합 — plan round 1 critic CRITICAL #2 의 발견 (re-export 제거 시 current module scope import 도 동시 사라져 unqualified call 깨짐). round 2 reflection 으로 Step 3 Action (d) 가 기존use kebab_core::{Block, BlockId, ...}block 에id_for_block, id_for_doc추가 명시. line 67 + 241 의 unqualified call resolve 보장.stage label 보존 정책의 일관성 —
tracing::debug!(target: "kebab-normalize", ...)(lib.rs:109) + 4 hard-codedagent: "kb-..."literal (lib.rs:122/128/134/143/153) 모두 보존. log scraper grep 일관성 + SQLitedocuments.provenance_jsonBLOB 의 audit log 일관성. spec round 2 critic NEW MAJOR #N1 (자동 module-path derive 가정 부정확) + NEW MAJOR #N2 (warning_agent body = "kb-parse-md" 단일 return vs 5 hard-coded literal 분리) 의 closure 가 executor 단계까지 정확 transcribe.9 hit fully-qualified path file-wide replace — executor self-detect (plan round 2) — actual
kebab-normalize/src/lib.rs의 9 hitkebab_parse_types::ParsedBlockKind::*(line 29 use + line 489/498/507/516/525/818/862/1070) 가 normalize.rs 이식 시 동반. sed file-wide replace 로crate::types::갈음 + exit gategrep -c "kebab_parse_types::" = 0검증.kebab-parse-md/tests/stale ref 누락 회피 — verifier round 1 BLOCKER #1 의 발견 (tests/blocks_snapshots.rs:19+frontmatter_snapshots.rs:23의 stale import) 가 round 2 reflection 으로 Step 5 file list + Action (c)(d) + exit gate scope 확장 (src/한정 해제). integration test 는crate::사용 불가 →kebab_parse_md::*사용으로 정확 갈음.frozen task spec 보존 + HOTFIXES live source 룰 준수 — CLAUDE.md "Task specs themselves stay frozen as the historical contract" rule 정확 적용.
git diff main..HEAD --name-only | grep "^tasks/p"= 0 line (~25 referencing task spec mechanical update 0). HOTFIXES 신규 entry (4-block 변형, design deviation — post-PR9 audit identified dead abstraction) + INDEX.md "Future work / deferred" 섹션 신설 + L169 v0.18.1+ defer mention closure + image/pdf normalize integration entry 한 줄. spec §11 cross-link 정확.workspace.package.version0.18.0 → 0.19.0 cascade — CLAUDE.md "Release / binary version bump" 의 "frozen design contract 변경 머지 후" trigger 정확. 22 kebab-* crate 모두version = { workspace = true }로 자동 cascade — release binary 262M 의 v0.19.0 stamp 확인.workspace 회귀 net delta = 0 의 정량 검증 — spec §5.1 expected baseline (1313) 과 post-refactor
cargo test --workspace --no-fail-fast -j 1결과 (1313 PASS, 0 failed) byte-identical match. 13 test 1:1 이동 (12 unit + 1 integration via normalize_snapshot.rs rename) + 모든 test 함수 보존. release binary 262M + 0 warning + 0 error.추가 actionable 없음. 26 file changed (production scope 27 unique paths), 모든 11 acceptance gate green. Wire / CLI / TUI / MCP / README 변경 0, 사용자 docpfood 영향 0. internal Rust crate-API surface 만 이동 (8 type + 2 fn re-export 가
kebab_parse_md::*로 symmetric 이동).머지 OK. 머지 후
gitea-release v0.19.0(CLAUDE.md "Release 절차" 룰) 으로 release tag 컷 권장 — frozen design contract 변경 + workspace.package version bump 가 새 release cut trigger. 다음 작업 (Lens 3 Extractor + Chunker dispatch unification, 1d medium risk) 동일 spec/plan/review/PR 사이클로 진행 — sonnet 모델 routing 정책 (closure verify round 2+) 적용.