Files
kebab/docs/superpowers/plans/2026-05-26-normalize-absorption-plan.md
altair823 710945c4b0 refactor(parse-md): absorb kebab-normalize + kebab-parse-types — 24 → 22 crates + §3.7b 재작성
design §3.7b 의 thin layer (ParsedBlock 류) 가 4 parser 중 1개 (markdown) 만 lift 를
경유하는 현실 — fan-in/fan-out 모두 1 → layer 의미 잃음. kebab-normalize (1097 LOC)
+ kebab-parse-types (98 LOC) 둘을 kebab-parse-md 로 흡수.

설계: docs/superpowers/specs/2026-05-26-normalize-absorption-spec.md
플랜: docs/superpowers/plans/2026-05-26-normalize-absorption-plan.md
HOTFIXES: tasks/HOTFIXES.md 의 2026-05-26 entry (design deviation)

- 5 사용 type + 3 forward-declared struct → kebab-parse-md::types module 의 pub explicit re-export.
- build_canonical_document + derive_title + warning_agent → kebab-parse-md::normalize module.
- 4 hard-coded agent literal (lib.rs:122/128/134/153) + warning_agent body return + tracing target literal 모두 보존 — stage label 일관성.
- kebab-app callsite (lib.rs:51 use + :1119 context string) + Cargo.toml 의 2 dep (regular + dead) 제거.
- kebab-chunk + kebab-store-sqlite 의 [dev-dependencies] kebab-normalize → 제거 (kebab-parse-md 로 갈음). 통합 test source 의 use shift.
- test file 이동 (kebab-normalize/tests/normalize_snapshot.rs → kebab-parse-md/tests/).
- workspace Cargo.toml: Hunk (a) members 2 entry 삭제 + Hunk (b) version 0.18.0 → 0.19.0 (frozen contract 변경).
- design §3.7b 4-단락 재작성 (원래 intent 보존 + 현재 상태 + 보존된 surface + future re-extraction trigger).
- design §8 graph 갱신 (3 edge 제거 + 2 forbidden bullet 의미 갱신 + commentary).
- ARCHITECTURE.md crate graph + directory tree mechanical 갱신.
- tasks/INDEX.md L169 closure mention + "Future work / deferred" 섹션 신설 (image/pdf normalize integration entry).
- tasks/HOTFIXES.md 신규 entry (4-block — design deviation Symptom).
- HANDOFF.md cross-link 한 줄.
- 3 dead struct (ParsedImageRegion / ParsedPdfPage / ParsedAudioSegment) 는 보존 — v0.20+ image/pdf normalize integration 의 future surface (spec §11).

Wire / surface impact: 0건. CLI / TUI / MCP / --json 출력 / config / XDG path /
parser_version 모두 unchanged. wire-invisible provenance.events[].agent + tracing target
literal "kb-normalize" 도 보존 — old DB row 와 new DB row 의 audit log 일관성.

Verification: cargo test --workspace --no-fail-fast -j 1 → 1313 passed / 0 failed (172 result blocks).
cargo clippy --workspace --all-targets -j 1 -- -D warnings → 0 warning (5m 46s).
cargo metadata --no-deps --format-version 1 | jq '.workspace_members | length' = 22.
cargo tree -p kebab-app --depth 2 | grep -E "kebab_(parse_types|normalize)" = 0 줄.
2026-05-26 15:00:59 +00:00

65 KiB
Raw Blame History

status, target_version, spec, contract_sections, related_specs, sibling_plan
status target_version spec contract_sections related_specs sibling_plan
open 0.19.0 docs/superpowers/specs/2026-05-26-normalize-absorption-spec.md
§3.7b
§8
docs/superpowers/specs/2026-04-27-kebab-final-form-design.md
docs/superpowers/specs/2026-05-26-source-fs-dep-lightening-spec.md
docs/superpowers/plans/2026-05-26-source-fs-dep-lightening-plan.md

kebab-normalize + kebab-parse-types 흡수 — implementation plan

spec round 3 APPROVE 직후의 plan (revision 2 — 10 step → 15 step decompose). spec §3.1-§3.10 의 결정 + §3.5/§3.6 의 design contract diff + §3.7 (a)-(g) callsite migration + §3.9 의 HOTFIXES wording + §5.1-§5.11 의 verification gate 가 step 단위로 분산. design + doc 갱신 (구 Step 9) 을 4 step (§3.7b 재작성 / §8 graph / ARCHITECTURE + INDEX + HOTFIXES + HANDOFF / version 사이드카 verify) 으로 split — critic + verifier 의 closure granularity 향상.

§0 Pre-flight + branch state

  • Branch: refactor/normalize-absorption (현재 위치, main d4395a3 위에서 분기 완료).
  • Base SHA: d4395a3 (PR #185 sibling — source-fs dep lightening 머지 직후, v0.18.0 cut 완료 시점).
  • Working dir: /home/altair823/kebab.
  • Env 강제 (CLAUDE.md disk-protection — ~/.claude/CLAUDE.md 의 "Disk Layout — 루트 디스크 보호가 최우선" 룰):
    • export CARGO_TARGET_DIR=/build/out/cargo-target/target — 본 plan 의 모든 cargo 명령에 적용. target/ 가 repo root 아래에 생성되지 않게 (16 GB RAM 머신 의 / 250 G 보호).
    • export TMPDIR=/build/cache/tmp — 대용량 임시 파일 발생 시 보호.
  • Cargo build 직렬화 (CLAUDE.md "Build / test / lint" + MEMORY.md feedback_serial_build_only.md):
    • 모든 cargo 명령 -j 1 강제. 18 integration-test binary 동시 link 시 OOM (linker SIGKILL).
    • per-crate cargo test -p <crate>-j 1 없어도 OK 이나 일관성을 위해 명시.
    • cargo test / clippy / build 동시 background 실행 금지. 하나 끝난 후 다음.
  • target/ clean policy (CLAUDE.md 룰 + spec §5.10): full workspace test 직전 cargo clean 1회 (Step 15). 중간 step (Step 2-14) 에서는 per-crate build 만 — cargo clean 불필요 (incremental cache 활용).
  • HOTFIXES.md / HANDOFF.md / README.md 변경 0 (spec §7 명시) — HOTFIXES.md 와 HANDOFF.md 는 본 plan 의 Step 14 에서 추가 (README 는 변경 0).
  • 4 frozen task spec (p1-2, p1-3, p1-4, p9-fb-07) 변경 0 + ~25 referencing task spec mechanical update 0 (spec §2 + §5.7).
  • wire schema 변경 0 (spec §1.9 verified, 16 wire schema 0 hit).
  • workspace Cargo.toml version bump 0.18.0 → 0.19.0 (Step 10 Hunk (b)).

§1 Approach summary

Spec §3 의 핵심 sequencing — destination = kebab-parse-md (spec §3.1, Option A):

  1. 신규 module 부터 작성 (Step 2-3) — kebab-parse-md/src/types.rs (parse-types 98 LOC 1:1 이식) + kebab-parse-md/src/normalize.rs (normalize 의 production fn body 이식, 4 hard-coded agent literal + tracing target literal 모두 보존).
  2. lib.rs 에 module + pub explicit re-export 등록 (Step 4) — 5 사용 type + 3 forward-declared struct + build_canonical_document / derive_title 의 surface 보존 (spec §3.3). explicit (glob 아님) — Q3 closure.
  3. blocks.rs + frontmatter.rs use 갱신 (Step 5) — use kebab_parse_types::*use crate::types::*. 동일 crate 내 in-source ref shift.
  4. kebab-app callsite + dep cleanup (Step 6-7, atomic 2 step) — lib.rs:51 use statement + lib.rs:1119 context string (Step 6) → Cargo.toml 의 2 dep 제거 (Step 7, kebab-normalize regular + kebab-parse-types dead regular — spec §3.10 incidental cleanup).
  5. dev-dep migration (Step 8) — kebab-chunk + kebab-store-sqlitekebab-normalize dev-dep 제거 (이미 kebab-parse-md dev-dep 보유). 통합 test source 의 use kebab_normalize::*;use kebab_parse_md::*;.
  6. test file 이동 + 자기 참조 verify (Step 9) — kebab-normalize/tests/normalize_snapshot.rskebab-parse-md/tests/normalize_snapshot.rs. 자기 참조 dev-dep declare 없는 cargo standard behavior verify.
  7. workspace Cargo.toml 갱신 — anchor step (Step 10) — Hunk (a) members 의 2 entry 삭제 + Hunk (b) workspace.package.version 0.18.0 → 0.19.0 (NIT #N6 closure).
  8. kebab-normalize/ + kebab-parse-types/ 디렉토리 삭제 (Step 11) — git rm -r. workspace 가 22 crate 로 collapse.
  9. design §3.7b 재작성 (Step 12) — spec §3.5 의 4-단락 wording 으로 design contract 의 line 703-764 replace.
  10. design §8 graph 갱신 (Step 13) — spec §3.6 의 diff (3 edge 제거 + 2 forbidden bullet 의미 갱신 + commentary) 적용.
  11. ARCHITECTURE + INDEX + HOTFIXES + HANDOFF 갱신 (Step 14) — 4 doc 의 mechanical update. INDEX.md 의 "Future work / deferred" 섹션 신설 (현재 부재 — §11.7).
  12. workspace 회귀 + clean commit — closure (Step 15) — cargo clean + 7 cargo gate + wire diff verify + 1 clean commit.

핵심 ordering invariant:

  • Step 2-3 < Step 4-5: destination module file 생성 후 lib.rs re-export — 둘 동시 commit 시 cargo build green 보장.
  • Step 4-5 < Step 6-7: in-crate ref shift 후 kebab-app callsite + dep — 외부 caller redirect 완료 후 dep 제거.
  • Step 6-7 < Step 8: kebab-app 정합 후 dev-dep migration — order independent 이나 sequential gate 분리.
  • Step 8 < Step 9: dev-dep cleanup 후 test file 이동 — git mv 가 git 의 add/remove 동시 commit 시 file content 보존.
  • Step 6-9 < Step 10: 모든 caller redirect 후 workspace.members 삭제 — 중간 build 깨짐 방지.
  • Step 10 < Step 11: workspace.members 제거 후 디렉토리 삭제 — stale path 회피.
  • Step 11 < Step 12-14: production code 갱신 완료 후 design contract + doc 갱신 — reality ≡ contract.
  • Step 12-14 < Step 15: 모든 doc 갱신 후 회귀 + commit — single clean commit 의 일관성.

§2 Steps (15 steps)

Step 1: Pre-flight baseline 측정 + env 확인

  • Files affected: 변경 0 (측정 only).
  • Action:
    • cd /home/altair823/kebab && git rev-parse HEADd4395a3 또는 그 위 commit 확인 (refactor branch 의 base).
    • env 확인: echo $CARGO_TARGET_DIR/build/out/cargo-target/target 인지. 비어있으면 §0 의 export 적용.
    • workspace baseline count 측정: cargo metadata --no-deps --format-version 1 | jq '.workspace_members | length'24 (현재 시점).
    • dead kebab-parse-types regular dep verify (spec §3.10): grep -n "kebab-parse-types" crates/kebab-app/Cargo.toml → 1 hit (line 16 부근), grep -rn "kebab_parse_types" crates/kebab-app/src/ → 0 hit.
    • baseline test count 측정 + persist (MAJOR GAP3 closure — spec §5.1 의 1313 + α 의 unit 정합화: test 함수 수 sum, NOT binary 수 행수):
      $ mkdir -p .omc/state
      $ cargo test --workspace --no-fail-fast -j 1 2>&1 \
          | awk '/^test result: ok\./ {for(i=1;i<=NF;i++) if($i=="passed;") sum += $(i-1)} END {print sum}' \
          > .omc/state/normalize-absorption-baseline.txt
      $ cat .omc/state/normalize-absorption-baseline.txt
      1313  # 예상 (spec §5.1)
      
      본 file 은 Step 15 의 numeric compare gate 의 source-of-truth. .omc/state/ 는 git untracked (gitignore default 또는 .omc/.gitignore).
  • Exit gate (cargo cli — falsifiable / observable / idempotent / scope-correct):
    • cargo metadata --no-deps --format-version 1 | jq '.workspace_members | length' = 24 (observable, idempotent).
    • cargo build --workspace -j 1 2>&1 | tail -5 의 마지막 라인 = Finished 또는 Compiling (현 시점 baseline green — falsifiable).
  • Spec 참조: §5.1 (baseline), §3.10 (dead dep verify).

Step 2: 신규 crates/kebab-parse-md/src/types.rs 생성 (98 LOC 1:1 이식)

  • Files affected:
    • crates/kebab-parse-md/src/types.rs (신규, ≈ 98 LOC, byte-identical 이식).
  • Action:
    • cp crates/kebab-parse-types/src/lib.rs crates/kebab-parse-md/src/types.rs 또는 Write tool 로 신규 생성.
    • 본문 = kebab-parse-types/src/lib.rs 의 98 LOC 의 1:1 이식:
      • 5 사용 type (ParsedBlock + ParsedBlockKind + ParsedPayload + Warning + WarningKind) 의 serde 표현 + variant 명 byte-identical 보존 (spec §2.2 의 non-goal — 의미 변경 0).
      • 3 forward-declared struct (ParsedImageRegion + ParsedPdfPage + ParsedAudioSegment) 보존 (spec §3.3 + §11.5 의 future surface).
      • module-level doc 의 §3.7b reference 보존 + 한 줄 추가: //! v0.19.0 부터 kebab-parse-md 의 in-crate module (이전 별 crate kebab-parse-types — HOTFIXES.md 2026-05-26 참조).
    • 본 step 은 추가 only — 기존 crates/kebab-parse-types/ 는 아직 alive. lib.rs 의 mod declare 가 없으므로 cargo 가 types.rs 무시 → build 깨지지 않음.
  • Exit gate:
    • wc -l crates/kebab-parse-md/src/types.rs ≈ 98 (≤ 105) — falsifiable.
    • grep -c "ParsedBlock\|ParsedBlockKind\|ParsedPayload\|Warning\|WarningKind\|ParsedImageRegion\|ParsedPdfPage\|ParsedAudioSegment" crates/kebab-parse-md/src/types.rs ≥ 8 — 8 type 모두 등장.
    • cargo build -p kebab-parse-md -j 1 2>&1 | tail -3 의 마지막 라인 = Finished (mod declare 없으므로 file 무시).
  • Spec 참조: §3.2 (module placement), §3.3 (visibility), §11.5 (future surface 보존).

Step 3: 신규 crates/kebab-parse-md/src/normalize.rs 생성 + Cargo.toml dep 갱신 (1097 LOC 이식 + literal 보존 + dep migration)

  • Files affected:
    • crates/kebab-parse-md/src/normalize.rs (신규, ≈ 1097 LOC, byte-identical 이식 + literal 보존 정책).
    • crates/kebab-parse-md/Cargo.toml (dep 갱신 — CRITICAL #1 + GAP2 closure).
  • Action:
    • (a) normalize.rs 생성cp crates/kebab-normalize/src/lib.rs crates/kebab-parse-md/src/normalize.rs 또는 Write 로 신규 생성.
    • 본문 = kebab-normalize/src/lib.rs 의 1097 LOC 의 production fn + comment + cfg(test) unit tests 1:1 이식:
      • build_canonical_document (signature (asset: &RawAsset, metadata: Metadata, blocks: Vec<ParsedBlock>, parser_version: &ParserVersion, warnings: Vec<Warning>) -> Result<CanonicalDocument>) 의 production body — spec §1.5 의 actual source byte-identical.
      • derive_title(frontmatter_title: &str, blocks: &[Block], file_stem: &str) -> String — spec §1.5 의 blocks: &[Block] (lifted, NOT ParsedBlock) inline 주석 보존.
      • warning_agent(kind: &WarningKind) -> &'static str 의 4 variant "kb-parse-md" 단일 return body 보존 (spec §3.7e + NEW MAJOR #N2 closure).
    • (b) literal 보존 (CRITICAL — spec §3.7 (e)(g) + §1.9 의 6-row trace table):
      line (post-port) string 보존?
      :109 (approx) target: "kebab-normalize" (tracing::debug! literal) ★ 보존
      :122 (approx) "kb-source-fs" (Discovered event agent) 보존
      :128 (approx) "kb-parse-md" (Parsed event agent) 보존
      :134 (approx) "kb-normalize" (Normalized event agent) ★ 보존
      :143 (approx) warning_agent(&w.kind).to_string() (Warning event agent — 동적, "kb-parse-md" 단일 return) 보존
      :153 (approx) "kb-normalize" (lift_warnings event agent) ★ 보존
    • (c) in-source ref shift — 3 갈래 (actual crates/kebab-normalize/src/lib.rs grep 결과 기반):
      -use kebab_parse_types::{ParsedBlock, ParsedPayload, Warning, WarningKind};
      +use crate::types::{ParsedBlock, ParsedPayload, Warning, WarningKind};
      
      추가로 cfg(test) mod tests 안의 9 hit fully-qualified call 도 갱신 (lib.rs:489, :498, :507, :516, :525, :818, :862, :1070 — 모두 kebab_parse_types::ParsedBlockKind::* 패턴):
      -kind: kebab_parse_types::ParsedBlockKind::Paragraph,
      +kind: crate::types::ParsedBlockKind::Paragraph,
      
      sed -i 's/kebab_parse_types::/crate::types::/g' crates/kebab-parse-md/src/normalize.rs (or Edit replace_all) — 9 hit 모두 mechanical 변경.
    • (d) pub use kebab_core::{id_for_block, id_for_doc} 제거 + in-body call 의 unqualified import 보존 (CRITICAL #2 closure): actual lib.rs:33pub usere-export + 동시 current module scope import. line 67 (id_for_doc(...)) + line 241 (id_for_block(...)) 의 unqualified call 이 pub use 의 scope import 에 의존. pub use 만 제거 시 unqualified call unresolved → compile error.
      -pub use kebab_core::{id_for_block, id_for_doc};
      +// (re-export 제거 — spec §3.3 R10 decision; production caller 0 verified.
      +//  in-body unqualified call 은 아래 use block 의 import 로 대체.)
      
      그리고 같은 file 의 기존 use kebab_core::{...} block 에 id_for_block, id_for_doc 추가 (실제 use block 의 정확한 위치는 actual lib.rsuse kebab_core::{ block 검색 후 정정 — Block / Metadata / SourceSpan 등의 normal import 와 함께 묶음):
       use kebab_core::{
           Block, BlockId, ...,
      +    id_for_block, id_for_doc,   // ← pub use 제거 후 in-body unqualified call 보존 (line 67, 241)
           ...
       };
      
    • (e) crates/kebab-parse-md/Cargo.toml dep 갱신 (CRITICAL #1 + GAP2 closure — spec §3.4):
       [package]
       name = "kebab-parse-md"
       ...
      -description = "Markdown frontmatter and block parsing into kb-core::Metadata / kb-parse-types intermediates"
      +description = "Markdown frontmatter + block parsing + canonical-document lift (absorbed kb-parse-types + kb-normalize, see HOTFIXES.md 2026-05-26)"
      
       [dependencies]
       kebab-core = { path = "../kebab-core" }
      -kebab-parse-types = { path = "../kebab-parse-types" }
       anyhow          = { workspace = true }
       serde           = { workspace = true }
       serde_json      = { workspace = true }
       time            = { workspace = true }
       tracing         = { workspace = true }
      +# 흡수된 kb-normalize 의 NFKC 의존 — actual kebab-normalize/src/lib.rs:31 의
      +# `use unicode_normalization::UnicodeNormalization;` 이 normalize.rs 이식 시 동반.
      +# 이미 kebab-app 도 사용 중인 0.1 major (version drift 0).
      +unicode-normalization = "0.1"
       pulldown-cmark  = { version = "0.13", default-features = false }
       ...
      
    • 본 step 도 추가 only — 기존 crates/kebab-normalize/ 는 아직 alive. lib.rs 의 mod declare (Step 4) 가 없으므로 normalize.rs file 무시되어 build 영향 0. 단 (e) 의 Cargo.toml 갱신은 Step 5/6 의 use shift 직후 fully active.
  • Exit gate:
    • wc -l crates/kebab-parse-md/src/normalize.rs ≈ 1097 (≤ 1110).
    • grep -c 'target: "kebab-normalize"' crates/kebab-parse-md/src/normalize.rs = 1 — tracing target literal 보존 (spec §3.7 (g) + R8 mitigation).
    • production agent literal 의 정확한 검증 (MINOR GAP6 — production body 의 agent: "kb-..." 패턴 grep): grep -cE 'agent:\s*"kb-(source-fs|parse-md|normalize)"\.to_string\(\)' crates/kebab-parse-md/src/normalize.rs ≥ 3 — 3 hard-coded literal (Discovered + Parsed + Normalized) 의 production body emission. warning_agent body 의 4 "kb-parse-md" return + lift_warnings 의 "kb-normalize" 합쳐 production agent literal 5 hit 보장.
    • grep -c "use crate::types::" crates/kebab-parse-md/src/normalize.rs ≥ 1 — in-source ref shift.
    • grep -c "kebab_parse_types::" crates/kebab-parse-md/src/normalize.rs = 0 — fully-qualified 9 hit 모두 갱신 (MAJOR #5 closure 의 normalize.rs 쪽).
    • grep -c "pub use kebab_core::" crates/kebab-parse-md/src/normalize.rs = 0 — re-export 제거 (spec §3.3 R10).
    • grep -E "use kebab_core::\{" crates/kebab-parse-md/src/normalize.rs | grep -c "id_for_block\|id_for_doc" ≥ 1 — id_for_* unqualified import 보존 (CRITICAL #2 closure).
    • grep -c "kebab-parse-types" crates/kebab-parse-md/Cargo.toml = 0 — Cargo.toml dep 제거.
    • grep -c "unicode-normalization" crates/kebab-parse-md/Cargo.toml ≥ 1 — Cargo.toml dep 추가 (CRITICAL #1 + GAP2 closure).
    • cargo build -p kebab-parse-md -j 1 green (mod declare 없으므로 normalize.rs file 무시. Cargo.toml dep 만 active).
  • Spec 참조: §3.2 (module placement), §3.3 (visibility), §3.4 (Cargo.toml dep diff), §3.7 (c)(d)(e)(g) (in-source ref + id_for_* + literal), §1.5 (signature), §1.9 (6-row trace), §3.3 R10 (id_for_*), CRITICAL #1 + #2 + NEW MAJOR #N1 + #N2 + MINOR GAP6 closure.

Step 4: crates/kebab-parse-md/src/lib.rs 갱신 (module declare + pub explicit re-export)

  • Files affected: crates/kebab-parse-md/src/lib.rs.
  • Action:
    • module-level doc 의 마지막에 한 줄 추가:
      //! v0.19.0 부터 `types` + `normalize` module 은 in-crate 흡수
      //! (`kebab-parse-types` + `kebab-normalize` 의 historical crate 가 본 crate 로
      //! collapse — see HOTFIXES.md 2026-05-26).
      
    • module declare + pub explicit re-export 추가 (spec §3.3 + §4.2 Q3 — glob 아님):
      mod types;
      mod normalize;
      
      // Spec §3.3 의 surface 보존 정책 — explicit (NOT glob) 으로 future addition leak 방지.
      pub use crate::types::{
          // 5 사용 type
          ParsedBlock, ParsedBlockKind, ParsedPayload,
          Warning, WarningKind,
          // 3 forward-declared struct (보존 — spec §3.3 + §11.5 future surface)
          ParsedImageRegion, ParsedPdfPage, ParsedAudioSegment,
      };
      pub use crate::normalize::{build_canonical_document, derive_title};
      
  • Exit gate:
    • grep -c "^mod types;\|^mod normalize;" crates/kebab-parse-md/src/lib.rs = 2.
    • grep "pub use crate::types" crates/kebab-parse-md/src/lib.rs | grep -c "ParsedImageRegion\|ParsedPdfPage\|ParsedAudioSegment" ≥ 1 — 3 forward-declared struct 의 explicit re-export 확인.
    • grep "pub use crate::normalize" crates/kebab-parse-md/src/lib.rs | grep -c "build_canonical_document\|derive_title" ≥ 1.
    • cargo build -p kebab-parse-md -j 1 green — 모듈 활성화 후 cargo 가 types.rs + normalize.rs 컴파일.
    • cargo test -p kebab-parse-md -j 1 green — 이식된 normalize unit test (~700 LOC) 도 in-crate 통과.
  • Spec 참조: §3.3 (visibility 정책), §4.2 Q3 (explicit vs glob — explicit 선택), §11.5 (future surface 보존).

Step 5: crates/kebab-parse-md/src/{blocks,frontmatter}.rs + tests/{blocks_snapshots,frontmatter_snapshots}.rs ref shift

  • Files affected:
    • crates/kebab-parse-md/src/blocks.rs (4 hit — actual line 1, 25, 37, 1589 verified).
    • crates/kebab-parse-md/src/frontmatter.rs (1+ hit — actual line 22 verified).
    • crates/kebab-parse-md/tests/blocks_snapshots.rs (BLOCKER #1 — actual line 19 verified).
    • crates/kebab-parse-md/tests/frontmatter_snapshots.rs (BLOCKER #1 — actual line 23 verified).
  • Action:
    • (a) blocks.rs — file-wide replace (MAJOR #5 closure, 4 hit):
      • line 1 doc comment: //! Markdown body → flat \Vec<kebab_parse_types::ParsedBlock>` (§3.4 / §3.7b).//! Markdown body → flat `Veccrate::types::ParsedBlock` (§3.4 / §3.7b).`
      • line 25 doc-link: [\kebab_parse_types::ParsedPayload::ImageRef`][`crate::types::ParsedPayload::ImageRef`]`
      • line 37 use: use kebab_parse_types::*; (또는 explicit list) → use crate::types::*; (또는 explicit list 갱신)
      • line 1589 production fully-qualified call: kebab_parse_types::ParsedBlockKind::Listcrate::types::ParsedBlockKind::List
      • Edit tool 의 replace_all: truekebab_parse_types::crate::types:: 단일 치환 가능 (4 hit + 가능한 hidden hit 모두 mechanical 갱신).
    • (b) frontmatter.rs — 동일 패턴:
      -use kebab_parse_types::{Warning, WarningKind};
      +use crate::types::{Warning, WarningKind};
      
      MalformedFrontmatter variant 의 fully-qualified call 등 추가 hit 검색 (grep -n "kebab_parse_types" crates/kebab-parse-md/src/frontmatter.rs 으로 확인 후 replace_all).
    • (c) tests/blocks_snapshots.rs (BLOCKER #1 — integration test 는 crate:: 사용 불가, kebab_parse_md::* 사용):
      -use kebab_parse_types::{ParsedBlock, Warning};
      +use kebab_parse_md::{ParsedBlock, Warning};
      
      line 19 의 use 갱신. integration test 는 자기 crate lib 자동 link → kebab_parse_md::* re-export 활성화 (Step 4 의 lib.rs explicit re-export 의 8 type 중 4 type 활용).
    • (d) tests/frontmatter_snapshots.rs (BLOCKER #1):
      -warnings: Vec<kebab_parse_types::Warning>,
      +warnings: Vec<kebab_parse_md::Warning>,
      
      line 23 의 fully-qualified type 갱신 (function signature param). 추가 hit 검색 (grep -n "kebab_parse_types" crates/kebab-parse-md/tests/frontmatter_snapshots.rs) 후 replace_all.
  • Exit gate:
    • grep -rn "kebab_parse_types" crates/kebab-parse-md/ = 0 hit (src/ + tests/ 모두 — verify cmd 의 src/ 한정 해제, BLOCKER #1 closure).
    • grep -c "crate::types::" crates/kebab-parse-md/src/blocks.rs ≥ 4 (line 1/25/37/1589 갱신).
    • grep -c "kebab_parse_md::" crates/kebab-parse-md/tests/blocks_snapshots.rs crates/kebab-parse-md/tests/frontmatter_snapshots.rs ≥ 2.
    • cargo build -p kebab-parse-md -j 1 green.
    • cargo test -p kebab-parse-md -j 1 green (integration test 의 fixture builder 가 self-link 통해 redirect).
  • Spec 참조: §3.7 (c) callsite migration (in-source ref shift), BLOCKER #1 + MAJOR #5 closure.

Step 6: crates/kebab-app/src/lib.rs:51, :1119 callsite migration

  • Files affected: crates/kebab-app/src/lib.rs.
  • Action:
    • (a) line 51 use statement (spec §3.7 (a)):
      -use kebab_normalize::build_canonical_document;
      +use kebab_parse_md::build_canonical_document;
      
    • (b) line 1119 context string (spec §3.7 (b) + MAJOR #4 closure):
      -        .context("kb-normalize::build_canonical_document")?;
      +        .context("kb-parse-md::build_canonical_document")?;
      
      주의: kb-parse-md::parse_frontmatter (line 1091) + kb-parse-md::parse_blocks (line 1099) 의 context string 은 변경 없음 — byte-identical hunk 적용 금지 (MAJOR #4 의 closure 명시).
  • Exit gate:
    • grep -n "kebab_normalize" crates/kebab-app/src/lib.rs = 0 hit (use 와 의 자취 모두 사라짐).
    • grep -n "kb-normalize::" crates/kebab-app/src/lib.rs = 0 hit (context string 갱신).
    • cargo build -p kebab-app -j 1 green — kebab-parse-md 의 re-export 가 alive 인 상태에서 redirect.
    • cargo test -p kebab-app -j 1 green.
  • Spec 참조: §3.7 (a)(b), MAJOR #4 closure.

Step 7: crates/kebab-app/Cargo.toml dep cleanup (2 dep 제거 — regular + dead incidental) — closure pre-pivot 1

  • Files affected: crates/kebab-app/Cargo.toml.
  • Action (spec §3.4 + §3.10): MINOR #3 closure — hunk 적용 전 actual context 확인 (sed 으로 idempotent):
    $ sed -n '11,20p' crates/kebab-app/Cargo.toml   # context 의 actual line 확인
    
    hunk:
     kebab-source-fs = { path = "../kebab-source-fs" }
     kebab-parse-md = { path = "../kebab-parse-md" }
    -kebab-parse-types = { path = "../kebab-parse-types" }
    -kebab-normalize = { path = "../kebab-normalize" }
     kebab-chunk = { path = "../kebab-chunk" }
    
    • kebab-normalize regular dep 제거 — Step 6 의 use shift 가 정합되어 더 이상 dep 불필요.
    • kebab-parse-types regular dep 제거 — dead dep (spec §3.10 incidental cleanup — Step 1 의 verify 에서 kebab_parse_types source import 0 hit 검증 완료).
  • Exit gate:
    • grep -E "kebab-normalize|kebab-parse-types" crates/kebab-app/Cargo.toml = 0 hit.
    • cargo build -p kebab-app -j 1 green.
    • cargo tree -p kebab-app --depth 2 | grep -E "kebab_(parse_types|normalize)" = 0 줄 — spec §5.2 의 anchor invariant.
  • Spec 참조: §3.4 (Cargo.toml diff), §3.10 (incidental cleanup), §5.2 (anchor invariant).

Step 8: crates/kebab-chunk/Cargo.toml + crates/kebab-store-sqlite/Cargo.toml dev-dep migration + 통합 test source use 갱신

  • Files affected:
    • crates/kebab-chunk/Cargo.toml.
    • crates/kebab-store-sqlite/Cargo.toml.
    • crates/kebab-chunk/tests/*.rs (use statement shift).
    • crates/kebab-store-sqlite/tests/*.rs (use statement shift).
  • Action:
    • (a) crates/kebab-chunk/Cargo.toml — spec §3.4:
       [dev-dependencies]
       # kb-parse-md / kb-normalize / kb-parse-code are dev-only — used by the
       # snapshot integration tests to build a CanonicalDocument from fixture files.
       # Forbidden as regular deps per design §8 (chunker consumes CanonicalDocument
       # from kb-core only); `cargo tree -p kb-chunk --depth 1` (default scope,
       # excludes dev-deps) confirms this.
       kebab-parse-md   = { path = "../kebab-parse-md" }
       kebab-parse-code = { path = "../kebab-parse-code" }
      -kebab-normalize  = { path = "../kebab-normalize" }
       serde_json       = { workspace = true }
       time             = { workspace = true }
      
      그리고 doc comment 의 kb-parse-md / kb-normalize / kb-parse-code mention 갱신 — kb-parse-md / kb-parse-code (kb-normalize 흡수 명시).
    • (b) crates/kebab-store-sqlite/Cargo.toml — spec §3.4:
       kebab-parse-md = { path = "../kebab-parse-md" }
      -kebab-normalize = { path = "../kebab-normalize" }
       kebab-chunk = { path = "../kebab-chunk" }
      
    • (c) 통합 test source 의 use statement 갱신 — actual grep 결과 명시 (MAJOR #2 closure, 2 file:line):
      • crates/kebab-chunk/tests/long_section_snapshot.rs:21use kebab_normalize::build_canonical_document;use kebab_parse_md::build_canonical_document;
      • crates/kebab-store-sqlite/tests/contract_roundtrip.rs:16use kebab_normalize::build_canonical_document;use kebab_parse_md::build_canonical_document; 추가 hit 검색 (grep -rn "kebab_normalize" crates/kebab-chunk/tests/ crates/kebab-store-sqlite/tests/) — 본 시점 의 verified hit = 2건. 새로 발견 시 동일 패턴으로 갱신.
  • Exit gate:
    • grep -l "kebab-normalize" crates/kebab-chunk/Cargo.toml crates/kebab-store-sqlite/Cargo.toml = 0 line.
    • grep -rn "kebab_normalize" crates/kebab-chunk/tests/ crates/kebab-store-sqlite/tests/ = 0 hit.
    • cargo test -p kebab-chunk -j 1 green — snapshot integration test 의 fixture builder 가 destination 으로 redirect.
    • cargo test -p kebab-store-sqlite -j 1 green — contract round-trip test.
  • Spec 참조: §3.4 (Cargo.toml diff), MAJOR #11 closure.

Step 9: test file 이동 (kebab-normalize/tests/kebab-parse-md/tests/) + 자기 참조 verify

  • Files affected (MAJOR #3 closure — actual find 결과 명시):
    • crates/kebab-normalize/tests/normalize_snapshot.rscrates/kebab-parse-md/tests/normalize_snapshot.rs.
    • actual find crates/kebab-normalize/tests/ -type f 결과 = normalize_snapshot.rs 단일 file (sibling snapshots/ 또는 fixtures/ 서브디렉토리 부재). 본 step 의 mv 대상 = 1 file only.
  • Action:
    • (a) git mv 로 mechanical move:
      # 실 실행 전 사전 verify (idempotent + observable):
      $ find crates/kebab-normalize/tests/ -type f
      crates/kebab-normalize/tests/normalize_snapshot.rs
      
      # 단일 file mv (sibling fixture 부재 확인됨):
      $ git mv crates/kebab-normalize/tests/normalize_snapshot.rs crates/kebab-parse-md/tests/normalize_snapshot.rs
      
    • (b) test file 의 use statement 갱신:
      • 기존 use kebab_normalize::*; 또는 use kebab_normalize::{build_canonical_document, derive_title};use kebab_parse_md::{build_canonical_document, derive_title}; 또는 in-crate crate::* 사용.
      • use kebab_parse_md::*; 가 이미 alive 이면 keep (자기 crate import 는 integration test 에서 cargo standard behavior 로 자동 link — spec §3.7 (f), R3 / Q4 closure).
      • kebab_parse_types::* 의 import 도 kebab_parse_md::* re-export 로 갈음.
    • (c) 자기 참조 dev-dep declare 제거 verify: 본 step 이후 kebab-normalize/ 디렉토리 자체가 Step 11 에서 삭제되므로 그 안의 Cargo.toml 의 kebab-parse-md = { path = "..." } dev-dep 도 vanish. 새 destination kebab-parse-md/Cargo.toml자기 참조 dev-dep 을 add 하지 않음 — cargo standard behavior (integration test 가 lib 자동 link).
  • Exit gate:
    • ls crates/kebab-parse-md/tests/normalize_snapshot.rs 존재.
    • ls crates/kebab-normalize/tests/normalize_snapshot.rs 2>&1 | grep -c "No such" = 1 (이동 완료).
    • cargo test -p kebab-parse-md --test normalize_snapshot -j 1 green — spec §3.7 (f) 의 cargo standard behavior verified (R3/Q4 closure).
    • grep "kebab-parse-md = " crates/kebab-parse-md/Cargo.toml = 0 hit (자기 참조 add 안 했는지 verify).
  • Spec 참조: §3.7 (f) (test file 이동 + cargo standard behavior), R3 (Q4 closure).

Step 10: workspace Cargo.toml Hunk (a) members + Hunk (b) version — anchor step

  • Files affected: Cargo.toml (workspace root).
  • Action (spec §3.8 + NIT #N6 closure — 2 hunk 분리):
    • Hunk (a) — [workspace] members 의 2 entry 삭제:
       [workspace]
       resolver = "3"
       members = [
           "crates/kebab-core",
      -    "crates/kebab-parse-types",
           "crates/kebab-config",
           "crates/kebab-source-fs",
           "crates/kebab-parse-md",
      -    "crates/kebab-normalize",
           "crates/kebab-chunk",
           ...
       ]
      
    • Hunk (b) — [workspace.package] version 1-line 변경:
       [workspace.package]
       edition       = "2024"
       rust-version  = "1.85"
       license       = "MIT OR Apache-2.0"
       repository    = "https://github.com/altair823/org/kebab"
      -version       = "0.18.0"
      +version       = "0.19.0"   # frozen design contract (§3.7b 재작성) 변경 trigger — CLAUDE.md "Release / binary version bump"
      
    • 두 hunk 는 sequential 또는 parallel 적용 가능 — line context 가 충분히 분리되어 있음 (NIT #N6 의 의도).
  • Exit gate:
    • cargo metadata --no-deps --format-version 1 | jq '.workspace_members | length' = 22 (spec §5.2 의 robust 명령).
    • grep '^version' Cargo.toml | head -1 = version = "0.19.0".
    • cargo build --workspace -j 1 green — Step 6-9 가 모든 dep path 참조 제거 완료한 상태이므로, members 에서 제외된 두 orphan 디렉토리 (crates/kebab-normalize/ + crates/kebab-parse-types/) 는 cargo 가 silently ignore. Cargo.lock 의 [[package]] entry 는 Step 11 직후 first cargo build 에서 자동 cleanup (MAJOR #1 closure — self-contradictory wording 제거).
  • Spec 참조: §3.8 (Hunk a + b), NIT #N6 closure, §5.2 (workspace count invariant).

Step 11: crates/kebab-normalize/ + crates/kebab-parse-types/ 디렉토리 삭제

  • Files affected:
    • crates/kebab-normalize/ (전체 디렉토리).
    • crates/kebab-parse-types/ (전체 디렉토리).
  • Action:
    • git rm -r crates/kebab-normalize/
    • git rm -r crates/kebab-parse-types/
    • cargo build --workspace -j 1 의 자동 Cargo.lock cleanup 으로 두 crate 의 [[package]] entry 사라짐 (Step 15 의 verification).
  • Exit gate:
    • ls crates/kebab-normalize/ 2>&1 | grep -c "No such" = 1.
    • ls crates/kebab-parse-types/ 2>&1 | grep -c "No such" = 1.
    • ls -d crates/*/ | wc -l = 22 (spec §5.2 의 secondary robust cmd).
    • cargo build --workspace -j 1 green — 모든 dep 정합.
  • Spec 참조: §3.8 (디렉토리 삭제).

Step 12: design §3.7b 4-단락 재작성 (docs/superpowers/specs/2026-04-27-kebab-final-form-design.md line 703-764)

  • Files affected: docs/superpowers/specs/2026-04-27-kebab-final-form-design.md (§3.7b 부분).
  • Action:
    • line 703-764 의 §3.7b 본문을 spec §3.5 의 wording 으로 replace (strike 아닌 재작성).
    • 4-단락 구조:
      1. 원래 의도 (v0.1~v0.18 머지 시점) — thin layer + medium-agnostic lift.
      2. 현재 상태 (v0.19.0~) — 흡수 근거 (4 parser 중 1개만 lift 경유, fan-in/fan-out 모두 1).
      3. 보존된 surface — 5 사용 type + 3 forward-declared struct → kebab-parse-mdpub re-export.
      4. future re-extraction trigger — 3 조건 명시 (fan-in ≥ 2 회복, ParsedBlock 변종 emit, medium-agnostic lift 일반화).
    • 의존 그래프 ascii 갱신 (post-absorb):
      kebab-core (도메인 모델 — Block, Chunk, SourceSpan, IDs, …)
         ▲
         │
      kebab-parse-md (markdown 의 frontmatter + block + types + normalize, 모두 in-crate)
         ▲
         │
      kebab-parse-pdf, kebab-parse-image, kebab-parse-code (자체 CanonicalDocument emit)
      
  • Exit gate:
    • sed -n '703,770p' docs/superpowers/specs/2026-04-27-kebab-final-form-design.md | grep -c "원래 의도\|현재 상태\|보존된 surface\|future re-extraction" ≥ 4 — 4 단락 모두 존재.
    • sed -n '703,770p' docs/superpowers/specs/2026-04-27-kebab-final-form-design.md | grep -c "thin layer\|fan-in" ≥ 1 — historical intent 의 wording 보존.
    • git diff main..HEAD -- docs/superpowers/specs/2026-04-27-kebab-final-form-design.md | head -100 의 hunk 가 line 703-764 만 touch (spec §5.6).
  • Spec 참조: §3.5 (4-단락 wording), §5.6 (design doc 갱신 검증).

Step 13: design §8 graph 갱신 (line 1457-1491) — 3 edge 제거 + 2 forbidden bullet 의미 갱신

  • Files affected: docs/superpowers/specs/2026-04-27-kebab-final-form-design.md (§8 부분).
  • Action (spec §3.6):
    • graph diff (3 edge 제거):
      -         ├─> kebab-parse-md / kebab-parse-pdf / kebab-parse-image / kebab-parse-audio
      -         │     └─> kebab-parse-types (parser intermediate)
      +         ├─> kebab-parse-md
      +         │     (post-v0.19.0: absorbed kebab-parse-types + kebab-normalize — §3.7b)
      +         ├─> kebab-parse-pdf / kebab-parse-image (self-emit CanonicalDocument)
                ├─> kebab-parse-code
                │     └─> kebab-core (domain types only — NO store/embed/llm/rag/UI)
      -         ├─> kebab-normalize
      -         │     └─> kebab-parse-types
                ├─> kebab-chunk
      
    • commentary 갱신 (§3.7b reference 의 thin layer wording 폐기):
      -`kebab-parse-types` 는 `kebab-core` 와 parsers/normalize 사이의 thin layer (§3.7b 참조).
      +`kebab-parse-md` 는 v0.19.0 부터 `kebab-parse-types` (parser intermediate types) 와 `kebab-normalize` (CanonicalDocument lift) 를 흡수한다 (§3.7b 참조). 4 parser 중 markdown 한 갈래만 lift 를 경유하므로 thin layer 의 가치가 의미를 잃었다. 보존된 5 사용 type + 3 forward-declared struct 의 surface 는 `kebab-parse-md` 의 `pub` re-export 로 backward-compat.
      
    • forbidden bullet 갱신:
       - UI → store/llm/parse 직접 의존 ✗
       - parse-* → store/llm/embed ✗
      -- parse-* → kebab-normalize ✗ (단방향: parsers → kebab-parse-types ← normalize)
      +- parse-* (pdf/image/code) → kebab-parse-md ✗ (parser 끼리 cross-import 금지 — markdown 의 lift 가 다른 parser 에 노출되면 안 됨)
       - chunk → llm/embed ✗
      -- normalize → store / parse-* ✗
      -- kebab-parse-types → 어떤 parser/normalize/store/llm/embed/search/rag/ui ✗ (`kebab-core` 만 의존)
       - 다른 store 와 cross-write ✗
      
      주의: kebab-parse-md → store / llm / embed ✗ 룰은 추가 안 함 (MAJOR #5 closure — 기존 parse-* → store/llm/embed ✗ 가 흡수된 lift 까지 자동 포함). 중복 룰 회피.
  • Exit gate:
    • sed -n '1457,1495p' docs/superpowers/specs/2026-04-27-kebab-final-form-design.md | grep -c "kebab-parse-types" ≤ 1 — 본문 commentary 의 historical reference 만 보존 (또는 0).
    • sed -n '1457,1495p' docs/superpowers/specs/2026-04-27-kebab-final-form-design.md | grep -c "kebab-normalize" ≤ 1 — 동상.
    • sed -n '1457,1495p' docs/superpowers/specs/2026-04-27-kebab-final-form-design.md | grep -c "parse-\\* (pdf/image/code) → kebab-parse-md ✗" = 1 — 신규 forbidden bullet 존재.
    • git diff main..HEAD -- docs/superpowers/specs/2026-04-27-kebab-final-form-design.md 의 hunk 가 §3.7b (Step 12) + §8 (Step 13) 의 2 section 만 touch — MINOR GAP7 closure 의 truncate-free numeric verify:
      • git diff main..HEAD -- docs/superpowers/specs/2026-04-27-kebab-final-form-design.md | grep -c "^@@" = 2 (정확히 2 hunk).
      • git diff main..HEAD -- docs/superpowers/specs/2026-04-27-kebab-final-form-design.md | grep -cE "^\+\+\+|^---" = 2 (file header — 1 file × 2 = 2).
  • Spec 참조: §3.6 (§8 graph diff), MAJOR #5 closure (중복 룰 회피).
  • Files affected:
    • docs/ARCHITECTURE.md (crate graph + directory tree).
    • tasks/INDEX.md (L169 closure mention + Future work 섹션 신설).
    • tasks/HOTFIXES.md (신규 entry — 4-block).
    • HANDOFF.md (1 줄 cross-link).
  • Action:
    • (a) docs/ARCHITECTURE.md 갱신 — spec §5.8:
      • crate graph 의 entries 24 → 22 — kebab-parse-types + kebab-normalize 절 삭제.
      • directory tree 의 crates/kebab-parse-types/ + crates/kebab-normalize/ 줄 삭제.
      • 흡수 mention 한 줄 추가 (또는 §3.7b strike 의 cross-link).
    • (b) tasks/INDEX.md L169 closure — spec §5.8:
      -  - **PR #181 chore: ... system-architect 의 component-level review 결론 = pre-cut nothing, all v0.18.1+ defer (kebab-normalize 흡수, Extractor dispatch unification, kebab-source-fs dep lightening 등).
      +  - **PR #181 chore: ... system-architect 의 component-level review 결론 = pre-cut nothing, all v0.18.1+ defer (kebab-normalize 흡수 — v0.19.0 closure, see HOTFIXES.md 2026-05-26; Extractor dispatch unification; kebab-source-fs dep lightening 등).
      
    • (c) tasks/INDEX.md "Future work / deferred" 섹션 신설 — spec §11.7 + verified INDEX.md 에 현재 부재:
      • 위치: "## Post-merge 핫픽스" 와 "## 모든 task 공통 규약" 사이 (신설).
      • 본문:
        ## Future work / deferred
        
        - v0.20+ image/pdf normalize integration — design §3.7b intent 미구현 (3 dead struct 보존). PR #186 (normalize-absorption) 의 spec §11 참조.
        
    • (d) tasks/HOTFIXES.md 신규 entry — spec §3.9 의 4-block 변형 그대로 (Symptom + Root cause + Action + Amends with inline Wire/surface impact). MAJOR #4 closure — anchor 검증 generic 화:
      # insertion point 의 generic verify (hard-coded entry 인용 회피):
      $ head -50 tasks/HOTFIXES.md   # frontmatter + heading + first entry 위치 확인
      $ FIRST_ENTRY_LINE=$(grep -n "^## 20" tasks/HOTFIXES.md | head -1 | cut -d: -f1)
      $ echo "insertion point = line ${FIRST_ENTRY_LINE} 의 *위* (chronological reverse — newest top)"
      
      본 시점 측정 결과 = line 17 (## 2026-05-26 — S3 NLI unavailable). 본 PR 의 entry 는 그 에 insert — 같은 2026-05-26 date 이지만 본 entry 는 source-fs sub-item 1 (PR #185) 의 sibling 인 sub-item 2 closure 라 chronologically later. Action 라인에 spec §11 cross-link 한 줄 포함 ("3 dead struct... 보존 — v0.20+ image/pdf normalize integration 의 future surface (spec §11 참조)").
    • (e) HANDOFF.md cross-link 한 줄## 머지 후 발견된 버그 / 결정 섹션 의 가장 최근 entry 위:
      - 2026-05-26 kebab-normalize + kebab-parse-types 흡수 (24 → 22 crates, design §3.7b 재작성). v0.19.0 cut. [HOTFIXES.md](tasks/HOTFIXES.md#2026-05-26--design-deviation--kebab-normalize--kebab-parse-types-흡수-24--22-crates).
      
  • Exit gate:
    • grep -c "Future work\|Future Work" tasks/INDEX.md ≥ 1 — 신설 섹션 존재.
    • grep -c "2026-05-26 — design deviation — kebab-normalize" tasks/HOTFIXES.md = 1 — entry 추가.
    • grep -c "image/pdf normalize integration" tasks/INDEX.md tasks/HOTFIXES.md ≥ 2 — 두 doc 모두 cross-link.
    • grep -c "2026-05-26 kebab-normalize" HANDOFF.md = 1.
    • grep -c "kebab-parse-types\|kebab-normalize" docs/ARCHITECTURE.md ≤ 1 — historical mention 만 보존 (또는 0).
    • 4 frozen task spec (p1-2, p1-3, p1-4, p9-fb-07) git diff main..HEAD 의 path 에 0 hit.
  • Spec 참조: §3.9 (HOTFIXES 4-block), §5.8 (ARCHITECTURE + INDEX), §11.7 (Future work 섹션 신설), §5.7 (frozen task spec invariant).

Step 15: workspace 회귀 + 7 cargo gate + clean commit — closure

  • Files affected: commit 만 (production code touch 0).
  • Action:
    • (a) cargo clean — spec §5.10 (16 GB RAM pressure 회피).
    • (b) full workspace test + numeric net-delta verify (GAP4 closure — Step 1 의 baseline 과 numeric compare):
      $ POST_COUNT=$(cargo test --workspace --no-fail-fast -j 1 2>&1 \
          | awk '/^test result: ok\./ {for(i=1;i<=NF;i++) if($i=="passed;") sum += $(i-1)} END {print sum}')
      $ echo "POST_COUNT=$POST_COUNT"
      $ diff <(echo "$POST_COUNT") .omc/state/normalize-absorption-baseline.txt \
          && echo "✓ net delta = 0 (spec §5.1)" \
          || { echo "✗ net delta != 0 — REQUIRES INVESTIGATION"; exit 1; }
      
      spec §5.1 의 expected net delta = 0 (1:1 lift, signature 무변, test 의미 무변 — Step 9 의 file 이동도 in-crate test 로 collapse 이므로 함수 수 보존). 만약 +N intentional addition 시 (예: NEW MAJOR #N1 + #N2 의 literal 보존 regression pin 신규) plan §1 approach summary 에 명시 + diff 의 numeric tolerance update.
    • (c) clippy gatecargo clippy --workspace --all-targets -- -D warnings 0 warning (spec §5.2).
    • (d) cargo denycargo deny check 0 error 0 warning (spec §5.9).
    • (e) 22 crate workspace verifycargo metadata --no-deps --format-version 1 | jq '.workspace_members | length' = 22 (spec §5.2 + §5.8).
    • (f) Cargo.lock 검증 — spec §5.11:
      $ grep '^name = "kebab-normalize"\|^name = "kebab-parse-types"' Cargo.lock
      (0 hit)
      $ awk '/^\[\[package\]\]/,/^$/{if(/name = "kebab-parse-md"/)f=1; if(f) print; if(/^$/ && f){f=0; print "---"}}' Cargo.lock | grep "unicode-normalization"
      unicode-normalization
      
    • (g) dep tree invariantcargo tree -p kebab-app --depth 2 | grep -E "kebab_(parse_types|normalize)" = 0 줄.
    • (h) wire schema diff = 0git diff main..HEAD -- docs/wire-schema/v1/ | wc -l = 0 (spec §5.4).
    • (i) clean commit:
      git add -A
      git status   # verify
      git commit -m "$(cat <<'EOF'
      refactor(parse-md): absorb kebab-normalize + kebab-parse-types — 24 → 22 crates + §3.7b 재작성
      
      design §3.7b 의 thin layer (ParsedBlock 류) 가 4 parser 중 1개 (markdown) 만 lift 를
      경유하는 현실 — fan-in/fan-out 모두 1 → layer 의미 잃음. kebab-normalize (1097 LOC)
      + kebab-parse-types (98 LOC) 둘을 kebab-parse-md 로 흡수.
      
      설계: docs/superpowers/specs/2026-05-26-normalize-absorption-spec.md
      플랜: docs/superpowers/plans/2026-05-26-normalize-absorption-plan.md
      HOTFIXES: tasks/HOTFIXES.md 의 2026-05-26 entry (design deviation)
      
      - 5 사용 type + 3 forward-declared struct → kebab-parse-md::types module 의 pub explicit re-export.
      - build_canonical_document + derive_title + warning_agent → kebab-parse-md::normalize module.
      - 4 hard-coded agent literal (lib.rs:122/128/134/153) + warning_agent body return + tracing target literal 모두 보존 — stage label 일관성.
      - kebab-app callsite (lib.rs:51 use + :1119 context string) + Cargo.toml 의 2 dep (regular + dead) 제거.
      - kebab-chunk + kebab-store-sqlite 의 [dev-dependencies] kebab-normalize → 제거 (kebab-parse-md 로 갈음). 통합 test source의 use shift.
      - test file 이동 (kebab-normalize/tests/normalize_snapshot.rs → kebab-parse-md/tests/).
      - workspace Cargo.toml: Hunk (a) members 2 entry 삭제 + Hunk (b) version 0.18.0 → 0.19.0 (frozen contract 변경).
      - design §3.7b 4-단락 재작성 (원래 intent 보존 + 현재 상태 + 보존된 surface + future re-extraction trigger).
      - design §8 graph 갱신 (3 edge 제거 + 2 forbidden bullet 의미 갱신 + commentary).
      - ARCHITECTURE.md crate graph + directory tree mechanical 갱신.
      - tasks/INDEX.md L169 closure mention + "Future work / deferred" 섹션 신설 (image/pdf normalize integration entry).
      - tasks/HOTFIXES.md 신규 entry (4-block — design deviation Symptom).
      - HANDOFF.md cross-link 한 줄.
      - 3 dead struct (ParsedImageRegion / ParsedPdfPage / ParsedAudioSegment) 는 보존 — v0.20+ image/pdf normalize integration 의 future surface (spec §11).
      
      Wire / surface impact: 0건. CLI / TUI / MCP / --json 출력 / config / XDG path /
      parser_version 모두 unchanged. wire-invisible provenance.events[].agent + tracing target
      literal "kb-normalize" 도 보존 — old DB row 와 new DB row 의 audit log 일관성.
      
      Verification: cargo test --workspace --no-fail-fast -j 1 green / cargo clippy --workspace
      --all-targets -- -D warnings 0 warning / cargo deny check 0 error / cargo metadata ...
      workspace_members | length = 22 / cargo tree -p kebab-app | grep kebab_parse_types
      + kebab_normalize = 0 줄.
      EOF
      )"
      
  • Exit gate:
    • 모든 cargo gate green (a-h).
    • git log --oneline -1 = 위 commit message 의 first line.
    • git status = clean (untracked file 0).
  • Spec 참조: §5.1-§5.11 (모든 verification gate).

§3 Step dependency graph

Step 1 (Pre-flight baseline)
   │
   ▼
Step 2 (types.rs 신설) ──┐
   │                     │ parallel OK (둘 다 in-crate, lib.rs mod declare 아직 없음)
   ▼                     │
Step 3 (normalize.rs 신설 + literal 보존) ◄─┘
   │
   ▼
Step 4 (lib.rs mod + pub explicit re-export)
   │
   ▼
Step 5 (blocks.rs + frontmatter.rs use shift)
   │
   ▼
Step 6 (kebab-app callsite lib.rs:51 + :1119)
   │
   ▼
Step 7 (kebab-app Cargo.toml dep cleanup) — anchor 1
   │
   ▼
Step 8 (kebab-chunk + kebab-store-sqlite dev-dep migration)
   │
   ▼
Step 9 (test file 이동 + 자기 참조 verify)
   │
   ▼
Step 10 (workspace Cargo.toml Hunk a + b) — anchor 2
   │
   ▼
Step 11 (kebab-normalize/ + kebab-parse-types/ 디렉토리 삭제)
   │
   ▼
Step 12 (design §3.7b 4-단락 재작성)
   │
   ▼
Step 13 (design §8 graph 3 edge 제거 + 2 forbidden bullet 갱신)
   │
   ▼
Step 14 (ARCHITECTURE + INDEX + HOTFIXES + HANDOFF)
   │
   ▼
Step 15 (회귀 + 7 cargo gate + clean commit) — closure

핵심 invariant:

  • Step 2 + 3 < Step 4: file 생성 후 lib.rs mod declare — 동시 commit 시 cargo 자동 link.
  • Step 4-5 < Step 6: 동일 crate 내 ref shift 후 외부 caller redirect.
  • Step 6 < Step 7: callsite migration 후 dep 제거 — kebab-app build green 보장.
  • Step 7 < Step 8: kebab-app 정합 후 dev-dep migration (sequential gate 분리, 자체 dep 영향 0).
  • Step 8 < Step 9: dev-dep cleanup 후 test file 이동.
  • Step 6-9 < Step 10: anchor 1 (kebab-app) + dev-dep + test file 모두 정합 후 anchor 2 (workspace.members).
  • Step 10 < Step 11: workspace.members 제거 후 디렉토리 삭제 — stale path 회피.
  • Step 11 < Step 12-14: production code 갱신 후 design + doc 갱신 — reality ≡ contract.
  • Step 12 < Step 13: §3.7b 재작성 후 §8 graph 갱신 (§8 commentary 가 §3.7b 인용).
  • Step 13 < Step 14: design 갱신 후 ARCHITECTURE + INDEX + HOTFIXES + HANDOFF 갱신 — design 이 source-of-truth, doc 들이 그 mirror.
  • Step 14 < Step 15: 모든 file change 후 commit.

§4 Verification gate (acceptance)

§4.1 Step 별 verify (per-step exit gate)

각 Step 의 "Exit gate" 항목이 step-local gate. 핵심 anchor:

  • Step 4 (lib.rs re-export): destination surface alive — cargo build -p kebab-parse-md green.
  • Step 7 (kebab-app dep cleanup): anchor 1 — cargo tree -p kebab-app | grep ... = 0 줄.
  • Step 10 (workspace.members): anchor 2 — cargo metadata ... | jq '.workspace_members | length' = 22.
  • Step 11 (디렉토리 삭제): final invariant — ls -d crates/*/ | wc -l = 22 + cargo build --workspace green.
  • Step 15 (closure): 7 cargo gate (a-h) + wire diff 0 + clean commit.

§4.2 Workspace 회귀 (spec §5.1, §5.2) — Step 15 시점

$ cd /home/altair823/kebab && export CARGO_TARGET_DIR=/build/out/cargo-target/target
$ cargo clean
$ cargo test --workspace --no-fail-fast -j 1   # baseline ± 작은 변동 (net delta = 0 또는 +N intentional)
$ cargo clippy --workspace --all-targets -- -D warnings   # 0 warning
$ cargo deny check   # 0 error 0 warning
$ cargo metadata --no-deps --format-version 1 | jq '.workspace_members | length'   # = 22
$ ls -d crates/*/ | wc -l   # = 22 (secondary)
$ cargo tree -p kebab-app --depth 2 | grep -E "kebab_(parse_types|normalize)"   # 0 line
$ grep -rn "kebab_normalize\|kebab_parse_types" crates/kebab-app/src/   # 0 hit
$ grep -l "kebab-normalize\|kebab-parse-types" crates/*/Cargo.toml   # 0 line
$ grep '^name = "kebab-normalize"\|^name = "kebab-parse-types"' Cargo.lock   # 0 hit

§4.3 Wire schema 회귀 (spec §5.4)

$ git diff main..HEAD -- docs/wire-schema/v1/ | wc -l   # = 0

§4.4 4 frozen task spec + ~25 referencing task spec frozen (spec §5.7)

$ git diff main..HEAD --name-only | grep -E "tasks/p1/p1-(2|3|4)|tasks/p9/p9-fb-07"   # 0 line
$ git diff main..HEAD --name-only | grep "^tasks/p" | wc -l   # 0 (모든 task spec mechanical update 0)

§4.5 SMOKE 회귀 (spec §5.5) — informational only

docs/SMOKE.md 가 정의한 isolated TempDir KB pipeline 의 ingest + search + ask 가 흡수 전후 byte-identical wire 출력. informational only — acceptance gate 아님 (production code 의 lift 로직 byte-identical 이므로 SMOKE 결과 자동 일관).

§4.6 Literal 보존 verify (spec §3.7 (e)(g) + NEW MAJOR #N1 + #N2)

Step 3 + Step 15 에서 두 번 verify:

$ grep -c 'target: "kebab-normalize"' crates/kebab-parse-md/src/normalize.rs   # = 1 (tracing target literal)
$ grep -E '"kb-(source-fs|parse-md|normalize)"' crates/kebab-parse-md/src/normalize.rs | wc -l   # >= 5 (4 hard-coded agent literal + warning_agent body return)

§5 Commit strategy

single clean commit — sibling plan (PR #185) 의 패턴. 본 PR 의 모든 change 가 atomic refactor 이므로 split 의 의미 없음.

  • Step 1-14 의 file edit 을 모두 staging 후 Step 15 의 commit message 로 single commit.
  • 중간 step 의 작업 progress 는 git stash 없이 working tree 에 누적 (cargo build/test green 유지 시 — exit gate 가 step 별 정합 보장).
  • Step 10-11 사이에 cargo build fail 또는 workspace 무시 발생 시 — expected behavior (§2 의 Step 10 exit gate 명시).
  • 만약 Step 11 의 디렉토리 삭제 후 unexpected build error 발생 시 — git reset --hard HEAD 으로 모든 working change drop 하고 plan 재진입 (sibling plan §6.1 와 동일 패턴).

Commit message 의 구조 (Step 15 의 (i) 참조):

  • first line: refactor(parse-md): absorb kebab-normalize + kebab-parse-types — 24 → 22 crates + §3.7b 재작성
  • 본문: 14 bullet (deliverable + wire/surface impact + verification) + Co-Authored-By 제거 (사용자 commit pattern 와 일관).

§6 Risks + mitigation

§6.1 중간 단계 cargo build 깨짐 (step ordering 깨짐)

위험: Step 6 (kebab-app callsite) 가 Step 2-4 (destination 생성) 보다 먼저 진행되면 use kebab_parse_md::build_canonical_document; 의 destination surface 가 alive 안 됨 → cargo build fail.

Mitigation: §3 step dependency graph 의 ordering invariant 명시. Step 별 exit gate 가 cargo build green 보장 — gate 통과 후 다음 step.

§6.2 Step 10 anchor 후 build fail (workspace.members 미정합)

위험: Step 10 의 Hunk (a) 만 적용하고 Step 11 디렉토리 삭제 안 하면 cargo 가 crates/kebab-normalize/Cargo.toml 의 path 를 lingering 으로 인식 가능 (단 members 에 없으므로 silently skip 예상).

Mitigation: Step 10 의 exit gate 가 "build fail 예상 또는 workspace 무시" 명시 — expected behavior. Step 11 즉시 진행으로 정합.

§6.3 자기 참조 dev-dep 의 cargo behavior 미검증

위험: Step 9 의 crates/kebab-parse-md/tests/normalize_snapshot.rs자기 자신lib 를 link 하는 패턴. cargo 의 standard behavior 는 dev-dep declare 없이 integration test 가 자기 crate lib 자동 link — 그러나 misconfig 시 silently no-link 위험.

Mitigation: spec §6.3 R3 + §3.7 (f) 의 명시 — cargo test -p kebab-parse-md --test normalize_snapshot -j 1 가 green 인지 Step 9 의 exit gate 에서 확인. 만약 fail 시 명시적 kebab-parse-md = { path = "." } dev-dep declare 추가 (cargo 의 어떤 edge case 일 수 있음).

§6.4 hard-coded literal accidental drop

위험: Step 3 의 normalize.rs 이식 시 4 hard-coded agent literal (lib.rs:122/128/134/153) + tracing target literal (lib.rs:109) 중 일부가 cp/Write 과정에서 누락. spec §3.7e + §3.7 (g) + §1.9 의 6-row production flow trace 정합 깨짐.

Mitigation: Step 3 의 exit gate 가 grep -c 'target: "kebab-normalize"' ... = 1 + grep -E '"kb-(source-fs|parse-md|normalize)"' ... | wc -l >= 5 명시. 5 literal (4 agent + 1 tracing target) 모두 grep 으로 verifiable. §4.6 의 spec-level verify gate 도 cross-check.

§6.5 ~25 referencing task spec 의 accidental edit

위험: Step 12-14 의 design + doc 갱신 시 ~25 referencing task spec 도 같이 검색-치환 하면 frozen 룰 위반.

Mitigation: spec §5.7 의 invariant — git diff main..HEAD --name-only | grep "^tasks/p" = 0 line. Step 14 종료 후 verify 로 확인. 만약 stray edit 발생 시 git checkout main -- tasks/p<N>/... 으로 revert.

§6.6 16 GB RAM 의 build pressure (full workspace test)

위험: Step 15 의 cargo test --workspace --no-fail-fast -j 1 가 lance / datafusion link step 에서 OOM 위험 (MEMORY.md feedback_serial_build_only.md + CLAUDE.md "Serial cargo builds only").

Mitigation:

  • cargo clean 직전 후 (Step 15 (a)) — stale artifact 제거로 link memory 절약.
  • -j 1 엄수 (sibling plan §6.6 패턴).
  • per-crate 단위 검증 우선 (Step 2-9 의 각 exit gate 가 -p <crate> 단위) — full workspace 는 Step 15 의 1회.
  • cargo test/clippy/build 동시 background 실행 금지.

§6.7 design §3.7b 재작성 wording 의 부정확

위험: Step 12 의 §3.7b 재작성 시 spec §3.5 의 4-단락 wording 을 정확히 따르지 않으면 critic round 5 (만약 spec 이 다회 revision) 의 verification 가 fail 가능.

Mitigation: Step 12 의 source-of-truth = spec §3.5 의 code block (한국어 산문 + 의존 그래프 ascii). plan/executor 가 spec §3.5 를 copy-paste 후 markdown formatting (heading depth 등) 조정.

§6.8 HOTFIXES.md entry 의 chronological reverse insert 실수

위험: Step 14 의 HOTFIXES entry 신규 추가 시 chronological reverse (newest top) 룰 위반 — 본 PR 의 entry 가 기존 entry 에 들어가면 reader 혼란.

Mitigation: Step 14 (d) 의 generic anchor verify cmd 활용 — head -50 tasks/HOTFIXES.md + FIRST_ENTRY_LINE=$(grep -n "^## 20" tasks/HOTFIXES.md | head -1 | cut -d: -f1) 으로 first entry line 위치 동적 측정 후 그 에 insert. hard-coded date / wording 인용 회피 — file 의 history 변경에도 robust.

§6.9 kebab-parse-md 의 dep 폭증 (spec §6.9 R9)

위험: 흡수 후 kebab-parse-md/Cargo.toml 의 deps 가 기존 + unicode-normalization (흡수 후 추가) 로 1개 증가. lingua 의 build time + binary size 가 markdown parse + lift 두 책임을 모두 가지는 crate 에 concentrate.

Mitigation:

  • 신규 deps = unicode-normalization 1 개만 (이미 kebab-app 도 사용 중인 0.1 major). version drift 없음.
  • Step 3 의 normalize.rs 이식 시 Cargo.toml[dependencies]unicode-normalization = "0.1" 추가 (sibling normalize 의 dep 와 동일 version).
  • 본 위험의 실질 영향 ≈ +1 dep → 영향 minimal.

§6.10 frozen p1-4 surface re-export 후퇴 (spec §6.10 R10)

위험: Step 3 의 normalize.rs 이식 시 pub use kebab_core::{id_for_block, id_for_doc} 제거 → p1-4 frozen public surface 의 후퇴.

Mitigation:

  • spec §6.10 R10 의 production caller 0 verified — re-export 경유 caller = 0 (test mod imports 제외, R10 의 grep cmd).
  • Step 3 의 exit gate 가 grep -c "pub use kebab_core::" crates/kebab-parse-md/src/normalize.rs = 0 verify — 명시적 제거.
  • p1-4 frozen 룰은 historical contract 로 보존 — 본 PR 의 HOTFIXES entry (Step 14) 가 live source.

§7 Out of scope (plan-level)

spec §8 의 모든 out-of-scope 그대로 + plan-level 추가:

  • sibling spec (PR #185 source-fs dep lightening) 의 follow-up. 별 PR / 별 plan.
  • kebab-parse-md 의 internal refactor (예: types.rs + normalize.rs 외 module 재배치). follow-up.
  • kebab-app/src/lib.rs 의 다른 callsite 의 cosmetic 변경 — line 51, 1119 만 touch.
  • Lens 3 (Extractor + Chunker dispatch unification) — 별도 작업 (spec §8 + §11 의 future direction sibling).
  • v0.20+ image/pdf normalize integration — spec §11 의 영구 보존 entry (본 PR 의 scope 외).
  • kebab-app/src/lib.rs 의 comment 안 historical kb-normalize mention (verified — line 1308 의 mirroring \kb-normalize::build_canonical_document`+ line 1474-1475 의see kb-normalize's `warning_agent``) — comment 의 historical reference 로 보존 (MINOR GAP8 closure). git blame 일관성 + reader 가 흡수 history 의 origin 추적 가능. context string (line 1119) 만 갱신 (Step 6) — production runtime behavior 의 wire-invisible 영향과 무관한 internal doc 형식의 historical reference 보존.

§8 References

  • spec: docs/superpowers/specs/2026-05-26-normalize-absorption-spec.md (1067 lines, round 3 APPROVE).
  • sibling plan: docs/superpowers/plans/2026-05-26-source-fs-dep-lightening-plan.md (PR #185 머지 완료).
  • design contract: docs/superpowers/specs/2026-04-27-kebab-final-form-design.md §3.7b + §8.
  • 4 frozen task spec: tasks/p1/p1-2-parser-types.md, tasks/p1/p1-3-markdown-parser.md, tasks/p1/p1-4-normalize.md, tasks/p9/p9-fb-07-md-title-fallback.md.
  • audit log root: tasks/INDEX.md L169 (PR #181 의 system-architect review).
  • CLAUDE.md (project) + CLAUDE.md (machine) + MEMORY.md — disk / cargo / commit 룰.

§9 Round closure status

§9.1 Round 1 status

Round Reviewer Verdict Issues Closure
1 critic-plan + verifier-plan (round 1) REQUEST_CHANGES (2 CRITICAL + 2 BLOCKER + 6 MAJOR + 5 MINOR + 1 NIT = 16) 본 round 2 revision 에서 all-closed — §9.2 의 finding-by-finding closure 참조 본 plan 의 round 2 revision (2026-05-26, planner)

§9.2 round 1 finding-by-finding closure

ID Severity Source Finding Closure (plan-level edit location)
#1 (critic) CRITICAL critic-plan kebab-parse-md/Cargo.toml 의 dep 갱신 step 부재 (spec §3.4 의 -kebab-parse-types + +unicode-normalization silently drop) CLOSED — Step 3 의 Files affected 에 crates/kebab-parse-md/Cargo.toml 추가 + Action (e) 신설 (description string 갱신 + kebab-parse-types 제거 + unicode-normalization = "0.1" 추가). Exit gate 의 grep -c "kebab-parse-types" Cargo.toml = 0 + grep -c "unicode-normalization" Cargo.toml ≥ 1.
#2 (critic) CRITICAL critic-plan pub use kebab_core::{id_for_block, id_for_doc} 제거 시 in-body unqualified call (line 67, 241) unresolved → compile error CLOSED — Step 3 의 Action (d) 갱신: pub use 제거 + 기존 use kebab_core::{...} block 에 id_for_block, id_for_doc 추가 명시. Exit gate grep -E "use kebab_core::\{" ... | grep -c "id_for_block|id_for_doc" ≥ 1. (대안 b: spec §3.3 retract — 채택 안 함, plan-side fix only)
#1 (verifier) BLOCKER verifier-plan kebab-parse-md/tests/kebab_parse_types import (2 hit verified — blocks_snapshots.rs:19 + frontmatter_snapshots.rs:23) 갱신 step 부재 CLOSED — Step 5 의 Files affected 에 2 test file 추가 + Action (c)(d) 신설 (kebab_parse_md::* 로 갈음, integration test 는 crate:: 사용 불가). Exit gate 의 grep scope src/ 한정 해제 → grep -rn "kebab_parse_types" crates/kebab-parse-md/ = 0 hit.
#5 (verifier GAP5) MAJOR verifier-plan blocks.rs 의 4 hit (line 1 doc, 25 doc-link, 37 use, 1589 production fully-qualified) 갱신 CLOSED — Step 5 의 Action (a) 의 file-wide replace 명시. 4 hit 모두 kebab_parse_types::crate::types:: (Edit replace_all: true 가능).
(extra finding) (planner self-detect) normalize.rs cfg(test) mod tests 안 9 hit fully-qualified kebab_parse_types::ParsedBlockKind::* (line 489/498/507/516/525/818/862/1070) 갱신 필요 CLOSED — Step 3 의 Action (c) 의 sed/replace_all 명시. Exit gate grep -c "kebab_parse_types::" crates/kebab-parse-md/src/normalize.rs = 0.
#1 (critic) MAJOR critic-plan Step 7 verify gate self-contradictory ("build fail 예상" — actual cargo behavior = silently ignore) CLOSED — Step 10 verify gate 의 wording 정정: "cargo build --workspace -j 1 green — 두 orphan 디렉토리는 cargo silently ignore. Cargo.lock 의 [[package]] entry 는 Step 11 직후 first cargo build 에서 자동 cleanup." (sibling plan revision 1 의 Step 7 → revision 2 의 Step 10)
#2 (critic) MAJOR critic-plan Step 5 의 generic glob → 2 file:line 명시 CLOSED — Step 8 의 Action (c) 갱신: chunk/tests/long_section_snapshot.rs:21 + store-sqlite/tests/contract_roundtrip.rs:16 explicit.
#3 (critic) MAJOR critic-plan Step 6 의 fixture file enumerate (find 결과 명시) CLOSED — Step 9 의 Files affected 갱신: find crates/kebab-normalize/tests/ 결과 = normalize_snapshot.rs 단일 file (sibling fixture 부재) 명시.
#4 (critic) MAJOR critic-plan HOTFIXES insertion anchor stale (hard-coded "S3 NLI" entry 인용) → generic 화 CLOSED — Step 14 의 Action (d) 갱신: head -50 tasks/HOTFIXES.md + FIRST_ENTRY_LINE=$(grep -n "^## 20" tasks/HOTFIXES.md | head -1) 동적 측정. §6.8 risk wording 도 generic 화.
GAP3 + GAP4 (verifier) MAJOR verifier-plan Step 1 의 baseline N + Step 10 의 net delta 0 numeric compare cmd 부정확 (binary 수 행수 ≠ test 함수 수) CLOSED — Step 1 의 baseline cmd 갱신: awk '/^test result: ok\./ {sum += "passed;" 직전 숫자} END {print sum}'. .omc/state/normalize-absorption-baseline.txt dump. Step 15 의 (b) numeric compare gate 추가: diff <(echo $POST_COUNT) .omc/state/normalize-absorption-baseline.txt.
GAP6 MINOR verifier-plan Step 2 (현 Step 3) 의 4-literal grep production-only 분리 CLOSED — Step 3 의 exit gate 의 grep cmd 정확화: grep -cE 'agent:\s*"kb-(source-fs|parse-md|normalize)"\.to_string\(\)' ≥ 3 (production body 의 hard-coded literal 만). warning_agent body return + lift_warnings literal 합쳐 5 hit 보장.
GAP7 MINOR verifier-plan Step 9 verify git diff ... | head -100 의 truncate 위험 CLOSED — Step 13 의 exit gate 갱신: head -200 삭제 + grep -c "^@@" = 2 + grep -cE "^\+\+\+|^---" = 2 numeric verify 추가.
GAP8 MINOR verifier-plan lib.rs comment stale kb-normalize mention (line 1308, 1474) 보존 결정 명시 CLOSED — §7 out-of-scope 에 한 줄 추가: "kebab-app/src/lib.rs 의 comment 안 historical kb-normalize mention 은 보존 — git blame 일관성 + reader 가 흡수 history 추적 가능. context string (line 1119) 만 갱신."
#3 (critic) MINOR critic-plan Step 4 (c) Cargo.toml hunk context — sed -n '11,20p' actual context 확인 명시 CLOSED — Step 7 의 Action 갱신: sed -n '11,20p' crates/kebab-app/Cargo.toml 사전 context 확인 cmd 추가.
#1 (critic) NIT critic-plan Step 7 "anchor" 명명 의미 — "closure pre-pivot" 정도 CLOSED — Step 7 의 heading 정정: "anchor" → "closure pre-pivot 1". Step 10 의 "anchor 2" 도 의미상 "closure pre-pivot 2" 로 변경 가능하나 plan 의 convention 일관성 유지 차원에서 "anchor 1" / "anchor 2" / "closure" 의 3-stage 명명 유지 (NIT 수준 — semantic only).

§9.3 Round 2 metrics

  • Plan line count: 567 (revision 1) → 761 (revision 2 start) → 현재 (revision 2 end, post-16-finding-closure).
  • Step count: 15 (revision 2 무변).
  • 신규 추가 (revision 2 end):
    • Step 3 의 Action (e) — kebab-parse-md/Cargo.toml diff.
    • Step 3 의 Action (d) — id_for_* unqualified import 보존.
    • Step 3 의 Action (c) — 9 hit fully-qualified 갱신.
    • Step 5 의 Action (c)(d) — kebab-parse-md/tests/ 의 2 file ref shift.
    • Step 1 + Step 15 의 baseline N + numeric compare.
    • §7 의 lib.rs comment 보존 명시.
    • §9.2 의 16-row closure table.
  • §3 Design 결정 무변 (Option A, dead struct 3 보존, §3.7b 4-단락 재작성, target_version 0.19.0, warning_agent + tracing target 보존 정책, id_for_* re-export 제거).

Plan drafted by: planner (team normalize-absorption, Phase B). Date: 2026-05-26. Source spec: 2026-05-26-normalize-absorption-spec.md (round 3 APPROVE, 1067 lines). Step count: 15 (decompose from 10 step revision 1). Step ordering invariant: Step 2 + 3 < Step 4-5 < Step 6 < Step 7 < Step 8 < Step 9 < Step 10 < Step 11 < Step 12 < Step 13 < Step 14 < Step 15. Anchor steps: Step 7 (kebab-app dep cleanup) + Step 10 (workspace.members) + Step 15 (closure). Estimated complexity: MEDIUM (15 step, 1:1 spec mapping, 단일 commit, ~3000 LOC code touch).