feat(p10-1B): Python + TS/JS AST chunkers — tree-sitter-{python,typescript,javascript} 코드 색인 활성화 #142

Merged
altair823 merged 18 commits from feat/p10-1b-py-ts-js into main 2026-05-20 02:26:28 +00:00
Owner

개요

1A-2 인프라 위에 Python + TypeScript + JavaScript 3 언어 AST chunker 활성화. design §1B 묶음과 일치하는 단일 PR. 머지 시점부터 Python / TS / JS 프로젝트도 dogfooding 가능 (이전: Rust만).

frozen design: docs/superpowers/specs/2026-05-15-kebab-code-ingest-design.md §1B.
task spec (frozen on merge): tasks/p10/p10-1b-py-ts-js-ast-chunkers.md.
plan: docs/superpowers/plans/2026-05-20-p10-1b-py-ts-js-ast-chunkers.md.

동결된 설계 결정 (이 PR로 확정)

  1. Symbol path 의 module prefix = workspace 경로 → module path 변환 (design §3.4 예시 충실, 사용자 명시 결정):
    • Python: kebab_eval/metrics.pydef compute_mrr() → symbol kebab_eval.metrics.compute_mrr. pkg/__init__.pypkg. module_path_for_python 가 source of truth.
    • TS/JS: src/Foo.tsFoo.bar method → symbol src/Foo.Foo.bar. export default function()<prefix>.default. module_path_for_tsjs 가 source of truth.
    • Rust 1A-2 는 retrofit 하지 않음 — 1A 는 file-scope nesting 만. 비일관 수용; HOTFIXES 2026-05-20 에 기록 + 사용자가 명시 요청 시 retrofit (chunker_version bump + reindex cascade).
  2. TS grammar 선택: tree_sitter_typescript::LANGUAGE_TYPESCRIPT (.ts) vs LANGUAGE_TSX (.tsx). 파일 확장자 기반 dispatch.
  3. JS grammar 단일: tree_sitter_javascript::LANGUAGE.js / .mjs / .cjs / .jsx 모두 처리.
  4. Expression-level 함수 (arrow fn, function expression assigned to const): 1B 1차 declaration-level 만 unit. const foo = () => {}<top-level> glue 로 포함. HOTFIXES 2026-05-20 기록; 후속 phase 에서 lexical_declaration unwrap 검토.
  5. App dispatch 일반화: ingest_one_code_asset(code_lang: &str) 가 4-arm match 로 lang 별 Extractor + Chunker 선택. Rust 기존 동작 byte-identical 보존 (기존 통합 테스트로 검증).
  6. Naming convention: parser/chunker 모두 약자 — code-{rust,python,ts,js}-v1 / code-{rust,python,ts,js}-ast-v1. design §3.3 표 일치.

변경 요약

  • Workspace deps: tree-sitter-python 0.25, tree-sitter-typescript 0.23, tree-sitter-javascript 0.25.
  • kebab-parse-code:
    • lang.rsmodule_path_for_python (dotted, src-root strip, __init__ fold) + module_path_for_tsjs (slash-style, ext strip only).
    • python.rsPythonAstExtractor + PARSER_VERSION = "code-python-v1". function_definition / class_definition (class-nesting mod_path) / decorated_definition unwrap (decorator 라인 fold). Glue (import/assignment/expression_statement/global) + <top-level> / <module> post-pass.
    • typescript.rsTypescriptAstExtractor + PARSER_VERSION = "code-ts-v1". TS + TSX dual grammar selection. interface_declaration / type_alias_declaration / enum_declaration 포함. export_statement unwrap + value field quirk 처리 (anonymous default export).
    • javascript.rsJavascriptAstExtractor + PARSER_VERSION = "code-js-v1". TS 의 strip-down 버전 (interface/type/enum 제거). JSX 도 같은 grammar.
  • kebab-chunk: code-python-ast-v1 / code-ts-ast-v1 / code-js-ast-v1 chunkers (1A-2 code-rust-ast-v1 near-duplicate, language-agnostic body 그대로). Cross-chunker policy_hash identity assert 됨.
  • kebab-source-fs: .py .pyipython, .ts .tsxtypescript, .js .mjs .cjs .jsxjavascript.
  • kebab-app: ingest_one_code_asset 일반화 (4-arm dispatch). 새 dep 없음. 4 통합 테스트 (Rust 3 보존 + Python/TS/JS 각 1).
  • 테스트: 4개 fixture (sample.{py,ts,tsx,js}) + per-lang extractor 단위 테스트 + 3 per-lang chunker snapshot 통합 테스트 (in-memory CanonicalDocument로 chunk boundary §6.3 준수).
  • docs: README (지원 형식 + Mermaid label), HANDOFF (phase row + 머지 후 결정 cross-link), ARCHITECTURE (디렉토리 트리 + 잠금 결정 표), SMOKE (P10-1B 절 추가), tasks/INDEX + tasks/p10/INDEX (status flip), frozen design §10.1 (한 줄).
  • HOTFIXES (2026-05-20, 2 항목):
    • Rust 1A-2 symbol path 비일관 (file-scope vs 1B+ workspace prefix).
    • Expression-level 함수 unit 제외 (1B 1차).
  • chore: workspace version 0.7.0 → 0.8.0 (도그푸딩 surface 확장 트리거, design §10.4). cascade 자동.

acceptance criteria 충족

  • cargo test --workspace --no-fail-fast -j 1 — green (0 failures).
  • cargo clippy --workspace --all-targets -- -D warnings — clean.
  • 4 fixture (Rust 1A-2 + Python/TS/JS 1B) ingest → chunk snapshot 안정 + Citation::Code symbol/line 이 §3.4 컨벤션 일치.
  • 격리 TempDir SMOKE: 3 lang 모두 kebab ingest → parser_version code-{python,ts,js}-v1 + code_lang_breakdown 에 3 lang 등장 + kebab search --code-lang {lang} --jsonCitation::Code { lang, symbol } 정상 반환.
  • frozen design §10.1 + 사용자 가시 docs 모두 동시 갱신.
  • HOTFIXES 2 entries dated cross-link 양방향.

커밋 (16)

44813df docs(p10-1b): README/HANDOFF/ARCHITECTURE/SMOKE/INDEX + HOTFIXES; chore: bump version 0.7.0 → 0.8.0
d6bb6cf test(p10-1b): per-language chunker snapshots (python/ts/js)
d53995a feat(p10-1b): code-js-ast-v1 chunker + activate JavaScript in app dispatch
c215034 feat(p10-1b): tree-sitter-javascript AST extractor (JS + JSX)
31245a4 fix(p10-1b): TS parser_version code-typescript-v1 → code-ts-v1 (naming consistency)
acb61b6 feat(p10-1b): activate TypeScript in ingest_one_code_asset dispatch
20feb31 feat(p10-1b): code-ts-ast-v1 chunker (1:1 + oversize split)
de63f16 feat(p10-1b): tree-sitter-typescript AST extractor (TS + TSX via grammar selection)
1815091 feat(p10-1b): activate Python in ingest_one_code_asset dispatch
6a0b340 feat(p10-1b): code-python-ast-v1 chunker (1:1 + oversize split)
9664e97 feat(p10-1b): tree-sitter-python AST extractor (PythonAstExtractor)
8bdb3e8 refactor(p10-1b): generalize ingest_one_code_asset for multi-language dispatch
dcad9cc feat(p10-1b): module_path_for_python / _tsjs helpers (workspace path → module prefix)
ed0f476 feat(p10-1b): route .py/.pyi/.ts/.tsx/.js/.mjs/.cjs/.jsx to MediaType::Code
0c61758 build(p10-1b): add tree-sitter-python/-typescript/-javascript workspace deps
39b766e docs(p10-1b): task spec + implementation plan

🤖 Generated with Claude Code

## 개요 1A-2 인프라 위에 **Python + TypeScript + JavaScript** 3 언어 AST chunker 활성화. design §1B 묶음과 일치하는 단일 PR. 머지 시점부터 Python / TS / JS 프로젝트도 dogfooding 가능 (이전: Rust만). frozen design: `docs/superpowers/specs/2026-05-15-kebab-code-ingest-design.md` §1B. task spec (frozen on merge): `tasks/p10/p10-1b-py-ts-js-ast-chunkers.md`. plan: `docs/superpowers/plans/2026-05-20-p10-1b-py-ts-js-ast-chunkers.md`. ## 동결된 설계 결정 (이 PR로 확정) 1. **Symbol path 의 module prefix = workspace 경로 → module path 변환** (design §3.4 예시 충실, 사용자 명시 결정): - **Python**: `kebab_eval/metrics.py` 의 `def compute_mrr()` → symbol `kebab_eval.metrics.compute_mrr`. `pkg/__init__.py` 는 `pkg`. `module_path_for_python` 가 source of truth. - **TS/JS**: `src/Foo.ts` 의 `Foo.bar` method → symbol `src/Foo.Foo.bar`. `export default function()` → `<prefix>.default`. `module_path_for_tsjs` 가 source of truth. - **Rust 1A-2 는 retrofit 하지 않음** — 1A 는 file-scope nesting 만. 비일관 수용; HOTFIXES 2026-05-20 에 기록 + 사용자가 명시 요청 시 retrofit (chunker_version bump + reindex cascade). 2. **TS grammar 선택**: `tree_sitter_typescript::LANGUAGE_TYPESCRIPT` (`.ts`) vs `LANGUAGE_TSX` (`.tsx`). 파일 확장자 기반 dispatch. 3. **JS grammar 단일**: `tree_sitter_javascript::LANGUAGE` 가 `.js` / `.mjs` / `.cjs` / `.jsx` 모두 처리. 4. **Expression-level 함수 (arrow fn, function expression assigned to const)**: 1B 1차 declaration-level 만 unit. `const foo = () => {}` 는 `<top-level>` glue 로 포함. HOTFIXES 2026-05-20 기록; 후속 phase 에서 `lexical_declaration` unwrap 검토. 5. **App dispatch 일반화**: `ingest_one_code_asset(code_lang: &str)` 가 4-arm `match` 로 lang 별 Extractor + Chunker 선택. Rust 기존 동작 byte-identical 보존 (기존 통합 테스트로 검증). 6. **Naming convention**: parser/chunker 모두 약자 — `code-{rust,python,ts,js}-v1` / `code-{rust,python,ts,js}-ast-v1`. design §3.3 표 일치. ## 변경 요약 - **Workspace deps**: `tree-sitter-python` 0.25, `tree-sitter-typescript` 0.23, `tree-sitter-javascript` 0.25. - **`kebab-parse-code`**: - `lang.rs` — `module_path_for_python` (dotted, src-root strip, `__init__` fold) + `module_path_for_tsjs` (slash-style, ext strip only). - `python.rs` — `PythonAstExtractor` + `PARSER_VERSION = "code-python-v1"`. `function_definition` / `class_definition` (class-nesting mod_path) / `decorated_definition` unwrap (decorator 라인 fold). Glue (import/assignment/expression_statement/global) + `<top-level>` / `<module>` post-pass. - `typescript.rs` — `TypescriptAstExtractor` + `PARSER_VERSION = "code-ts-v1"`. TS + TSX dual grammar selection. `interface_declaration` / `type_alias_declaration` / `enum_declaration` 포함. `export_statement` unwrap + `value` field quirk 처리 (anonymous default export). - `javascript.rs` — `JavascriptAstExtractor` + `PARSER_VERSION = "code-js-v1"`. TS 의 strip-down 버전 (interface/type/enum 제거). JSX 도 같은 grammar. - **`kebab-chunk`**: `code-python-ast-v1` / `code-ts-ast-v1` / `code-js-ast-v1` chunkers (1A-2 `code-rust-ast-v1` near-duplicate, language-agnostic body 그대로). Cross-chunker policy_hash identity assert 됨. - **`kebab-source-fs`**: `.py` `.pyi` → `python`, `.ts` `.tsx` → `typescript`, `.js` `.mjs` `.cjs` `.jsx` → `javascript`. - **`kebab-app`**: `ingest_one_code_asset` 일반화 (4-arm dispatch). 새 dep 없음. 4 통합 테스트 (Rust 3 보존 + Python/TS/JS 각 1). - **테스트**: 4개 fixture (`sample.{py,ts,tsx,js}`) + per-lang extractor 단위 테스트 + 3 per-lang chunker snapshot 통합 테스트 (in-memory CanonicalDocument로 chunk boundary §6.3 준수). - **docs**: README (지원 형식 + Mermaid label), HANDOFF (phase row + 머지 후 결정 cross-link), ARCHITECTURE (디렉토리 트리 + 잠금 결정 표), SMOKE (P10-1B 절 추가), tasks/INDEX + tasks/p10/INDEX (status flip), frozen design §10.1 (한 줄). - **HOTFIXES (2026-05-20, 2 항목)**: - Rust 1A-2 symbol path 비일관 (file-scope vs 1B+ workspace prefix). - Expression-level 함수 unit 제외 (1B 1차). - **chore**: workspace version `0.7.0 → 0.8.0` (도그푸딩 surface 확장 트리거, design §10.4). cascade 자동. ## acceptance criteria 충족 - `cargo test --workspace --no-fail-fast -j 1` — green (0 failures). - `cargo clippy --workspace --all-targets -- -D warnings` — clean. - 4 fixture (Rust 1A-2 + Python/TS/JS 1B) ingest → chunk snapshot 안정 + `Citation::Code` symbol/line 이 §3.4 컨벤션 일치. - 격리 TempDir SMOKE: 3 lang 모두 `kebab ingest` → parser_version `code-{python,ts,js}-v1` + `code_lang_breakdown` 에 3 lang 등장 + `kebab search --code-lang {lang} --json` → `Citation::Code { lang, symbol }` 정상 반환. - frozen design §10.1 + 사용자 가시 docs 모두 동시 갱신. - HOTFIXES 2 entries dated cross-link 양방향. ## 커밋 (16) ``` 44813df docs(p10-1b): README/HANDOFF/ARCHITECTURE/SMOKE/INDEX + HOTFIXES; chore: bump version 0.7.0 → 0.8.0 d6bb6cf test(p10-1b): per-language chunker snapshots (python/ts/js) d53995a feat(p10-1b): code-js-ast-v1 chunker + activate JavaScript in app dispatch c215034 feat(p10-1b): tree-sitter-javascript AST extractor (JS + JSX) 31245a4 fix(p10-1b): TS parser_version code-typescript-v1 → code-ts-v1 (naming consistency) acb61b6 feat(p10-1b): activate TypeScript in ingest_one_code_asset dispatch 20feb31 feat(p10-1b): code-ts-ast-v1 chunker (1:1 + oversize split) de63f16 feat(p10-1b): tree-sitter-typescript AST extractor (TS + TSX via grammar selection) 1815091 feat(p10-1b): activate Python in ingest_one_code_asset dispatch 6a0b340 feat(p10-1b): code-python-ast-v1 chunker (1:1 + oversize split) 9664e97 feat(p10-1b): tree-sitter-python AST extractor (PythonAstExtractor) 8bdb3e8 refactor(p10-1b): generalize ingest_one_code_asset for multi-language dispatch dcad9cc feat(p10-1b): module_path_for_python / _tsjs helpers (workspace path → module prefix) ed0f476 feat(p10-1b): route .py/.pyi/.ts/.tsx/.js/.mjs/.cjs/.jsx to MediaType::Code 0c61758 build(p10-1b): add tree-sitter-python/-typescript/-javascript workspace deps 39b766e docs(p10-1b): task spec + implementation plan ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
altair823 added 16 commits 2026-05-20 01:49:33 +00:00
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rust path observably unchanged (verified by existing code_ingest_smoke tests).
Python/TS/JS arms bail with TODO; per-lang extractor + chunker land in subsequent tasks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Duplicate of code-rust-ast-v1 with language-agnostic body unchanged. Cross-chunker
policy_hash identity asserted vs md-heading-v1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces Python bail! arms with PythonAstExtractor + CodePythonAstV1Chunker.
Adds python_file_ingests_and_searches_as_code_citation integration test —
asserts citation.lang=python, symbol=kebab_eval.metrics.compute_mrr,
code_lang=python. TS/JS arms remain bail!() (Tasks J/L).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds `kebab_parse_code::typescript::TypescriptAstExtractor` (PARSER_VERSION
`code-typescript-v1`), mirroring the Python extractor (P10-1B Task E) and
the Rust scaffold (P10-1A-2). One `Block::Code` per top-level AST semantic
unit (free fn / class / each method / interface / type alias / enum,
recursively per nested class), each carrying `SourceSpan::Code` with the
unit's dotted symbol path prefixed by `module_path_for_tsjs`.

Grammar selection per `tree-sitter-typescript` 0.23: the workspace path's
`.tsx` extension routes to `LANGUAGE_TSX`, everything else to
`LANGUAGE_TYPESCRIPT`. The `export_statement` arm unwraps a `declaration`
field (`function_declaration` / `class_declaration` / `interface_declaration`
/ `type_alias_declaration` / `enum_declaration`) using the OUTER statement's
line range so `export ` is folded in; for `export default function () {}`
and `export default class {}` (where the inner node sits under the `value`
field as `function_expression` / `class` with no `name`), the symbol leaf
is `default`. Bare value exports / re-exports fall into glue.

Glue grouping reuses the Python post-pass: `<module>` only when the entire
group is imports + bare re-exports; demoted to `<top-level>` if the file
produced any real unit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Duplicate of code-rust-ast-v1 / code-python-ast-v1 with language-agnostic body unchanged.
Cross-chunker policy_hash identity asserted vs md-heading-v1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces TS bail! arms with TypescriptAstExtractor + CodeTsAstV1Chunker.
Adds typescript_file_ingests_and_searches_as_code_citation integration test —
asserts citation.lang=typescript, symbol=src/Foo.Foo.bar, code_lang=typescript.
JS arms remain bail!() (Task L).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task H implementer chose code-typescript-v1 but plan + design §3.3 use the
short form (chunker is code-ts-ast-v1 / code-js-ast-v1). Aligning parser
versions to match: rust=code-rust-v1 / python=code-python-v1 / ts=code-ts-v1
/ js=code-js-v1 (Task K). Fixes 2 sites: const PARSER_VERSION + integration
test assertion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single-grammar variant of typescript.rs — JS handles .jsx via the same
LanguageFn. No interface/type/enum arms; otherwise identical mapping +
workspace-path prefix via module_path_for_tsjs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Chunker: duplicate-with-substitution from code-ts-ast-v1 / code-rust-ast-v1.
Dispatch: replaces JS bail! arms with JavascriptAstExtractor + CodeJsAstV1Chunker.
Integration test javascript_file_ingests_and_searches_as_code_citation asserts
citation.lang=javascript, symbol=src/Bar.Bar.baz, code_lang=javascript.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors code_rust_ast_snapshot pattern. In-memory CanonicalDocument build so
no kebab-parse-code dep (boundary §6.3 respected).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 requested changes 2026-05-20 01:57:32 +00:00
claude-reviewer-01 left a comment
Member

회차 1 — 전반적으로 견고한 PR. 경계 규칙 / 명명 / HOTFIXES 양방향 cross-link 모두 충족. 6개 actionable 항목을 기록한다.

개요

1A-2 인프라 위에 3 언어 extractor + chunker 를 올리는 패턴이 일관되게 적용됨. 경계 검사 통과 — kebab-chunk 의 tree-sitter dep 없음, kebab-parse-code 는 store/embed/llm/rag/UI 에 직접 의존 안 함, kebab-app 에 새 grammar dep 없음. 명명(code-ts-v1, code-js-v1, code-python-v1, 대응 *-ast-v1 chunker) 일관; 커밋 31245a4 수정 후 stale code-typescript-v1 잔재 없음. HOTFIXES 2026-05-20 두 항목 양방향 cross-link 정상. snapshot 베이스라인 well-formed (3 × 8 chunks, 필수 필드 완비, bigXxx oversize 분할 확인됨). 통합 테스트 카운트: Rust 3개 (보존) + Python 1 + TS 1 + JS 1 = 6개.

Actionable 항목 (6)

  1. [missing-hotfix] module_path_for_python/_tsjs path-sanitize 부재 — task spec Risks/notes 에서 HOTFIXES 기록을 약속했지만 실제 HOTFIXES 엔트리 없음. task spec p10-1b-py-ts-js-ast-chunkers.md Risks/notes: "HOTFIXES 에 path-sanitize 부재 기록" — HOTFIXES.md (2026-05-20) 에 해당 엔트리 없음. 세 번째 HOTFIX 엔트리 추가 필요 (비-ASCII / 공백 / 특수문자 workspace_path 미처리).

  2. [nit] filename_from_workspace_path + strip_extension 세 파일 중복. python.rs, typescript.rs, javascript.rs 각각 동일 두 함수 보유. 1B 1차 범위에서 수용 가능하지만, lang.rspub(crate) 로 올리거나 별도 utils.rs 로 분리하면 후속 Go/Java/Kotlin (1C) 추가 시 또 복제하지 않아도 됨. 강제 수정보다 향후 1C PR 전 정리 권고.

  3. [edge-case] walk_class_body (TS/JS)가 class 내부 decorated method 를 누락. TS 의 @Input() method() / JS 의 @log method() 같은 decorated class method 는 tree-sitter 에서 method_definition 가 아니라 decorator + method_definition 형태로 나타날 수 있음. 현재 walk_class_bodychild.kind() == "method_definition" 만 처리 — Python 은 walk() 재귀로 decorated_definition 을 올바르게 처리하지만 TS/JS 는 그렇지 않음. 1B 범위에서 수용은 가능하지만 Python 과의 비대칭이 놀라울 수 있으므로 HOTFIXES 또는 인라인 // TODO 권고.

  4. [edge-case] module_path_for_pythontests/ / examples/ 경로를 strip 하지 않음. tests/test_foo.py → module prefix tests.test_foo (strip 안 됨). src/lib/ 는 strip, tests/는 아님. PR body 에서 "narrow keep" 결정을 언급했으나 HOTFIXES 에도 docs 에도 기록 없음. 사용자가 Python 테스트 파일을 dogfood KB 에 포함시키면 symbol 이 tests.test_foo.TestCase.test_method 식으로 노출 — 놀라울 수 있음. 결정 명시 권고 (HOTFIXES 한 줄 or SMOKE.md 알려진 동작 bullet).

  5. [nit] sample.py fixture 에 module-level @staticmethod 사용. Python 에서 @staticmethod 는 class method decorator — module-level 함수에 붙이면 syntactically valid 하지만 semantically 잘못된 코드. fixture 가 tree-sitter 처리 자체는 통과시키지만, 실제 코드에서는 발생하지 않는 패턴. 후속 reviewer 가 의아하게 볼 수 있으므로 @cache / @functools.lru_cache 같은 실제 쓰이는 decorator 로 교체 권고.

  6. [missing-test] Python module_path_for_pythontests/ / examples/ 경로 단위 테스트 없음. strip 안 함이 의도된 결정이라면 assert_eq!(module_path_for_python("tests/test_foo.py"), "tests.test_foo") 한 줄로 문서화 겸 회귀 방지. 현재 테스트 벡터 7개에 해당 케이스 없음.

PRAISE

  • 경계 분리 완벽 (kebab-chunk no tree-sitter dep, kebab-app no grammar dep, kebab-parse-code no store/embed).
  • commit 31245a4 으로 TS 명명 수정(code-typescript-v1code-ts-v1) 후 전체 소스/테스트/스냅샷/docs 에 stale string 없음 — 철저한 sweep.
  • snapshot baseline 구성 우수: 7개 unit + bigXxx oversize → 2-part split, doc_id / block_id 재현 가능, 세 언어 동일 구조.
회차 1 — 전반적으로 견고한 PR. 경계 규칙 / 명명 / HOTFIXES 양방향 cross-link 모두 충족. 6개 actionable 항목을 기록한다. ## 개요 1A-2 인프라 위에 3 언어 extractor + chunker 를 올리는 패턴이 일관되게 적용됨. 경계 검사 통과 — `kebab-chunk` 의 tree-sitter dep 없음, `kebab-parse-code` 는 store/embed/llm/rag/UI 에 직접 의존 안 함, `kebab-app` 에 새 grammar dep 없음. 명명(`code-ts-v1`, `code-js-v1`, `code-python-v1`, 대응 `*-ast-v1` chunker) 일관; 커밋 `31245a4` 수정 후 stale `code-typescript-v1` 잔재 없음. HOTFIXES 2026-05-20 두 항목 양방향 cross-link 정상. snapshot 베이스라인 well-formed (3 × 8 chunks, 필수 필드 완비, bigXxx oversize 분할 확인됨). 통합 테스트 카운트: Rust 3개 (보존) + Python 1 + TS 1 + JS 1 = 6개. ## Actionable 항목 (6) 1. **[missing-hotfix] `module_path_for_python`/`_tsjs` path-sanitize 부재 — task spec Risks/notes 에서 HOTFIXES 기록을 약속했지만 실제 HOTFIXES 엔트리 없음.** task spec `p10-1b-py-ts-js-ast-chunkers.md` Risks/notes: "HOTFIXES 에 path-sanitize 부재 기록" — HOTFIXES.md (2026-05-20) 에 해당 엔트리 없음. 세 번째 HOTFIX 엔트리 추가 필요 (비-ASCII / 공백 / 특수문자 workspace_path 미처리). 2. **[nit] `filename_from_workspace_path` + `strip_extension` 세 파일 중복.** `python.rs`, `typescript.rs`, `javascript.rs` 각각 동일 두 함수 보유. 1B 1차 범위에서 수용 가능하지만, `lang.rs` 에 `pub(crate)` 로 올리거나 별도 `utils.rs` 로 분리하면 후속 Go/Java/Kotlin (1C) 추가 시 또 복제하지 않아도 됨. 강제 수정보다 향후 1C PR 전 정리 권고. 3. **[edge-case] `walk_class_body` (TS/JS)가 class 내부 decorated method 를 누락.** TS 의 `@Input() method()` / JS 의 `@log method()` 같은 decorated class method 는 tree-sitter 에서 `method_definition` 가 아니라 `decorator + method_definition` 형태로 나타날 수 있음. 현재 `walk_class_body` 는 `child.kind() == "method_definition"` 만 처리 — Python 은 `walk()` 재귀로 `decorated_definition` 을 올바르게 처리하지만 TS/JS 는 그렇지 않음. 1B 범위에서 수용은 가능하지만 Python 과의 비대칭이 놀라울 수 있으므로 HOTFIXES 또는 인라인 `// TODO` 권고. 4. **[edge-case] `module_path_for_python` 이 `tests/` / `examples/` 경로를 strip 하지 않음.** `tests/test_foo.py` → module prefix `tests.test_foo` (strip 안 됨). `src/` 와 `lib/` 는 strip, `tests/`는 아님. PR body 에서 "narrow keep" 결정을 언급했으나 HOTFIXES 에도 docs 에도 기록 없음. 사용자가 Python 테스트 파일을 dogfood KB 에 포함시키면 symbol 이 `tests.test_foo.TestCase.test_method` 식으로 노출 — 놀라울 수 있음. 결정 명시 권고 (HOTFIXES 한 줄 or SMOKE.md 알려진 동작 bullet). 5. **[nit] `sample.py` fixture 에 module-level `@staticmethod` 사용.** Python 에서 `@staticmethod` 는 class method decorator — module-level 함수에 붙이면 syntactically valid 하지만 semantically 잘못된 코드. fixture 가 tree-sitter 처리 자체는 통과시키지만, 실제 코드에서는 발생하지 않는 패턴. 후속 reviewer 가 의아하게 볼 수 있으므로 `@cache` / `@functools.lru_cache` 같은 실제 쓰이는 decorator 로 교체 권고. 6. **[missing-test] Python `module_path_for_python` 에 `tests/` / `examples/` 경로 단위 테스트 없음.** strip 안 함이 의도된 결정이라면 `assert_eq!(module_path_for_python("tests/test_foo.py"), "tests.test_foo")` 한 줄로 문서화 겸 회귀 방지. 현재 테스트 벡터 7개에 해당 케이스 없음. ## PRAISE - 경계 분리 완벽 (`kebab-chunk` no tree-sitter dep, `kebab-app` no grammar dep, `kebab-parse-code` no store/embed). - commit `31245a4` 으로 TS 명명 수정(`code-typescript-v1` → `code-ts-v1`) 후 전체 소스/테스트/스냅샷/docs 에 stale string 없음 — 철저한 sweep. - snapshot baseline 구성 우수: 7개 unit + `bigXxx` oversize → 2-part split, doc_id / block_id 재현 가능, 세 언어 동일 구조.
@@ -0,0 +1,221 @@
//! Snapshot test pinning the `Vec<Chunk>` JSON for a

[PRAISE] snapshot 테스트 설계 일관성 우수. 1A-2 Rust snapshot 과 동일 패턴 — kebab-parse-code 를 dev-dep 으로 두지 않고 in-memory Block::Code 직접 구성으로 경계를 지킨다. big_compute / bigTransform / BigProcessor oversize 분할까지 커버하고, 세 언어 모두 8 chunks 로 구조 일관. policy_hash_matches_md_heading_v1 cross-chunker 불변 테스트도 모든 새 chunker 에 포함되어 있다.

**[PRAISE] snapshot 테스트 설계 일관성 우수.** 1A-2 Rust snapshot 과 동일 패턴 — `kebab-parse-code` 를 dev-dep 으로 두지 않고 in-memory `Block::Code` 직접 구성으로 경계를 지킨다. `big_compute` / `bigTransform` / `BigProcessor` oversize 분할까지 커버하고, 세 언어 모두 8 chunks 로 구조 일관. `policy_hash_matches_md_heading_v1` cross-chunker 불변 테스트도 모든 새 chunker 에 포함되어 있다.
@@ -43,0 +44,4 @@
/// p10-1B: workspace-relative Python file path → dotted module-path prefix.
/// See plan §Task C for the exact rules + tasks/p10/p10-1b for the §3.4
/// design contract.
pub fn module_path_for_python(workspace_path: &str) -> String {

[PRAISE] module_path_for_python / module_path_for_tsjs 테스트 커버리지 우수. __init__.py fold, src/ strip, crates/*/src/ strip, .pyi extension 등 7개 edge case 벡터가 잘 구성돼 있다. TS/JS 쪽도 6개 extension 모두 테스트됨.

**[PRAISE] `module_path_for_python` / `module_path_for_tsjs` 테스트 커버리지 우수.** `__init__.py` fold, `src/` strip, `crates/*/src/` strip, `.pyi` extension 등 7개 edge case 벡터가 잘 구성돼 있다. TS/JS 쪽도 6개 extension 모두 테스트됨.
@@ -43,0 +89,4 @@
use super::*;
#[test]
fn module_path_for_python_strips_src_roots_and_extensions() {

[edge-case + missing-test] module_path_for_pythontests/ 를 strip 하지 않는 동작이 테스트 / docs 에 명시되지 않았다. 현재 7개 테스트 벡터에 tests/test_foo.pytests.test_foo (strip 안 됨) 케이스가 없다. assert_eq!(module_path_for_python("tests/test_foo.py"), "tests.test_foo") 한 줄을 추가하면 좁은 strip 정책이 의도된 것임을 코드로 문서화할 수 있다. 아울러 SMOKE.md 알려진 동작 bullet 또는 HOTFIXES 한 줄로 사용자에게 알릴 것을 권고한다.

**[edge-case + missing-test] `module_path_for_python` 이 `tests/` 를 strip 하지 않는 동작이 테스트 / docs 에 명시되지 않았다.** 현재 7개 테스트 벡터에 `tests/test_foo.py` → `tests.test_foo` (strip 안 됨) 케이스가 없다. `assert_eq!(module_path_for_python("tests/test_foo.py"), "tests.test_foo")` 한 줄을 추가하면 좁은 strip 정책이 의도된 것임을 코드로 문서화할 수 있다. 아울러 SMOKE.md 알려진 동작 bullet 또는 HOTFIXES 한 줄로 사용자에게 알릴 것을 권고한다.
@@ -0,0 +159,4 @@
}
}
fn filename_from_workspace_path(p: &str) -> String {

[nit] 중복 private helpers. filename_from_workspace_path + strip_extension + join_symbol 세 함수가 python.rs / typescript.rs / javascript.rs 에 동일하게 3회 복제돼 있다. 1B 1차 범위에서는 수용 가능하지만 1C (Go/Java/Kotlin) PR 이전에 lang.rspub(crate) 로 올리거나 별도 utils.rs 로 추출하면 네 번째 복제를 막을 수 있다.

**[nit] 중복 private helpers.** `filename_from_workspace_path` + `strip_extension` + `join_symbol` 세 함수가 `python.rs` / `typescript.rs` / `javascript.rs` 에 동일하게 3회 복제돼 있다. 1B 1차 범위에서는 수용 가능하지만 1C (Go/Java/Kotlin) PR 이전에 `lang.rs` 에 `pub(crate)` 로 올리거나 별도 `utils.rs` 로 추출하면 네 번째 복제를 막을 수 있다.
@@ -0,0 +271,4 @@
) {
let mut cur = body.walk();
for child in body.named_children(&mut cur) {
if child.kind() == "method_definition" {

[edge-case] walk_class_body 가 decorated class method 를 누락할 수 있다. child.kind() == "method_definition" 만 처리한다. TypeScript 에서 @Injectable() method(): void {} 같은 class-level decorator 가 있으면 tree-sitter 는 decorator + method_definition 형태로 파싱할 수 있어 method 가 누락된다. Python 은 walk() 재귀로 decorated_definition 을 올바르게 처리하므로 언어 간 비대칭이 발생한다. 1B 범위에서 수용은 가능하지만 HOTFIXES 한 줄이나 인라인 // TODO(p10-1c): handle decorated methods in walk_class_body 주석으로 명시 권고.

**[edge-case] `walk_class_body` 가 decorated class method 를 누락할 수 있다.** `child.kind() == "method_definition"` 만 처리한다. TypeScript 에서 `@Injectable() method(): void {}` 같은 class-level decorator 가 있으면 tree-sitter 는 `decorator + method_definition` 형태로 파싱할 수 있어 method 가 누락된다. Python 은 `walk()` 재귀로 `decorated_definition` 을 올바르게 처리하므로 언어 간 비대칭이 발생한다. 1B 범위에서 수용은 가능하지만 HOTFIXES 한 줄이나 인라인 `// TODO(p10-1c): handle decorated methods in walk_class_body` 주석으로 명시 권고.
@@ -0,0 +3,4 @@
ANSWER = 42
@staticmethod

[nit] @staticmethod 를 module-level 함수에 적용 — semantically 잘못된 Python. @staticmethod 는 class method decorator 다. module-level 함수에 붙이면 런타임에 TypeError: 'staticmethod' object is not callable 이 발생한다 (tree-sitter 파싱은 통과하지만). fixture 의 의도가 decorator unwrap 테스트라면 @functools.lru_cache 같은 실제로 사용되는 decorator 로 교체가 더 적절하다.

**[nit] `@staticmethod` 를 module-level 함수에 적용 — semantically 잘못된 Python.** `@staticmethod` 는 class method decorator 다. module-level 함수에 붙이면 런타임에 `TypeError: 'staticmethod' object is not callable` 이 발생한다 (tree-sitter 파싱은 통과하지만). fixture 의 의도가 decorator unwrap 테스트라면 `@functools.lru_cache` 같은 실제로 사용되는 decorator 로 교체가 더 적절하다.
@@ -17,0 +36,4 @@
**proper fix**: `lexical_declaration` visitor 에서 binding value 가 `arrow_function` / `function` expression 인 경우 해당 identifier name 을 symbol 로 사용하는 unwrap 추가. 후속 phase 에서 검토.
**cross-link**: `tasks/p10/p10-1b-py-ts-js-ast-chunkers.md` Risks / notes 섹션.

[missing-hotfix] task spec p10-1b-py-ts-js-ast-chunkers.md Risks/notes 에 "HOTFIXES 에 path-sanitize 부재 기록"이라고 명시했지만 이 파일에 해당 엔트리가 없다. module_path_for_python / module_path_for_tsjs 가 비-ASCII / 공백 / 특수문자가 포함된 workspace_path 를 sanitize 없이 그대로 통과시키는 편차를 2026-05-20 세 번째 항목으로 추가해야 한다 — 다른 두 항목과 동일한 형식으로. spec 이 기록을 약속했으므로 이번 PR 에서 완결하는 것이 좋다.

**[missing-hotfix]** task spec `p10-1b-py-ts-js-ast-chunkers.md` Risks/notes 에 "HOTFIXES 에 path-sanitize 부재 기록"이라고 명시했지만 이 파일에 해당 엔트리가 없다. `module_path_for_python` / `module_path_for_tsjs` 가 비-ASCII / 공백 / 특수문자가 포함된 workspace_path 를 sanitize 없이 그대로 통과시키는 편차를 2026-05-20 세 번째 항목으로 추가해야 한다 — 다른 두 항목과 동일한 형식으로. spec 이 기록을 약속했으므로 이번 PR 에서 완결하는 것이 좋다.
altair823 added 1 commit 2026-05-20 02:04:00 +00:00
(1) tasks/HOTFIXES.md: add 2026-05-20 entry for path-sanitize gap in
    module_path_for_python / _tsjs (promised in task spec line 55 but
    not landed in round 0). Bidirectional cross-link added.

(2) crates/kebab-parse-code: dedup filename_from_workspace_path /
    strip_extension / join_symbol via new pub(crate) module scaffold.rs.
    Removed 9 byte-identical fn copies across rust/python/typescript/
    javascript extractors. Pure refactor — no behavior change.

(3) crates/kebab-parse-code/tests/fixtures/sample.py: @staticmethod was
    semantically inappropriate on a module-level fn (class-method
    decorator). Changed to @no_type_check; test assertion updated.

(5)+(6) crates/kebab-parse-code/src/lang.rs: add tests/test_foo.py case
    to module_path_for_python test + doc clarifying that tests/ /
    examples/ / benches/ are intentionally not stripped.

(4) PUSH BACK — TS/JS class decorator handling is design intent of 1B
    1차 (typescript.rs:242-244 + HOTFIXES entry 2 already in place).
    No code change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 requested changes 2026-05-20 02:14:05 +00:00
claude-reviewer-01 left a comment
Member

회차 2 — 5개 round-1 수정 검증 완료, push-back 부분 반박, 신규 1건 (데코레이터 라인 범위 비일관성)

Round-1 수정 검증 결과 (5건)

# 항목 결과 비고
1 HOTFIXES 2026-05-20 path-sanitize 엔트리 날짜 형식 일관, cross-link 양방향 확인 (HOTFIXES → spec line 55, spec line 60 → HOTFIXES)
2 scaffold.rs — 3 helper pub(crate) 추출 함수 3개 pub(crate), 구현 byte-identical 확인; lib.rs 에서 pub(crate) mod scaffold; 올바름; cargo test -p kebab-parse-code 17 passed 0 failed
3 sample.py @staticmethod → @no_type_check 픽스처 line 6 @no_type_check 반영; python.rs line 429 어서션이 @no_type_check 포함 체크로 갱신
5+6 lang.rs: tests/test_foo.py 어서션 + NOT-stripped 문서화 line 108 테스트 케이스 추가, line 49-52 doc 주석 명확화

Item (4) Push-back 평가 — 부분 반박

author 의 push-back 은 "TS/JS 클래스 메서드 데코레이터를 독립 unit 으로 방출하지 않는 것은 design intent" 라고 주장하며 HOTFIXES entry 2 (expression-level 함수) 와 typescript.rs 주석을 근거로 제시합니다. 이 주장 자체는 옳습니다.

그러나 reviewer 가 지적한 문제의 차원을 잘못 파악했습니다. 원래 우려는 "decorated class method 를 독립 unit 으로 방출하지 않는 것"이 아니라, 이미 방출되는 method_definition unit 의 line_start@decorator 라인을 포함하지 않는다는 것입니다. 이는 Python 과의 cross-language 비일관성입니다.

Python: decorated_definition 래퍼 노드가 존재하며 walk() 가 이를 만나면 outer node 의 start_position(= @decorator 라인)을 unit 의 line_start 로 사용합니다. 클래스 body 에서도 동일하게 @classmethod def name() unit 은 @classmethod 라인부터 시작합니다.

TypeScript/JavaScript: walk_class_body 는 class_body 의 named_children 을 순회할 때 decorator 노드(kind="decorator")를 무시하고 지나칩니다. method_definition 을 만나면 unit_start(&child) 를 호출하는데, 이 함수는 prev_sibling 을 역방향 순회하며 "comment" 종류만 흡수하고 "decorator" 를 만나면 즉시 중단합니다. 결과적으로 @Log\nmethod() {} 에서 @Log 라인은 unit 의 line range 밖입니다.

HOTFIXES entry 2 는 expression-level 함수(arrow fn / const assignment)에 관한 것이며, 클래스 메서드 데코레이터 라인 범위에 대한 항목은 존재하지 않습니다. 이 비일관성은 미기록 상태입니다.


Round-2 신규 fresh pass 결과

  • snapshot naming (code-typescript-v1 / code-javascript-v1 잔류물): 없음
  • scaffold.rs visibility: pub(crate) 적절, 외부 노출 불필요
  • module_path_for_python / _tsjs determinism: 순수 문자열 연산, 비결정성 없음
  • TS/JS vs Python symbol separator 컨벤션 (/ vs .): design §3.4 표에 TS/JS: module/Class.method 로 명시, 의도적 선택
  • app dispatch try_skip_unchanged: ingest_one_code_asset 에서 lang 분기 후 parser_version / chunker_version 을 per-lang 으로 올바르게 선택해 전달
  • HOTFIXES 날짜 형식: ## 2026-05-20 — 형식, 기존 엔트리와 일관

결론

safety / correctness 관점에서 1건 의 실질적 문제 발견:

typescript.rs + javascript.rsunit_start()"decorator" prev_sibling 을 흡수하지 않아 TS/JS 클래스 메서드 데코레이터 라인이 unit range 밖으로 떨어집니다. Python 의 동작과 비일관하며 HOTFIXES 에 미기록입니다. 아래 두 옵션 중 하나로 처리하면 됩니다:

  • Option A (권장): unit_start() 의 prev_sibling 순회에서 "decorator""comment" 와 동일하게 흡수 (typescript.rs + javascript.rs 각 3줄 추가). Python 동작과 일치.
  • Option B: HOTFIXES 에 신규 항목 추가 후 defer — 비일관성이 영구화될 위험 있음.

나머지 round-1 수정 5건은 모두 검증됐습니다. 이 1건 처리 후 round 3 은 APPROVE 가능 예상입니다.

회차 2 — 5개 round-1 수정 검증 완료, push-back 부분 반박, 신규 1건 (데코레이터 라인 범위 비일관성) ## Round-1 수정 검증 결과 (5건) | # | 항목 | 결과 | 비고 | |---|------|------|------| | 1 | HOTFIXES 2026-05-20 path-sanitize 엔트리 | ✅ | 날짜 형식 일관, cross-link 양방향 확인 (HOTFIXES → spec line 55, spec line 60 → HOTFIXES) | | 2 | scaffold.rs — 3 helper pub(crate) 추출 | ✅ | 함수 3개 pub(crate), 구현 byte-identical 확인; lib.rs 에서 pub(crate) mod scaffold; 올바름; `cargo test -p kebab-parse-code` 17 passed 0 failed | | 3 | sample.py @staticmethod → @no_type_check | ✅ | 픽스처 line 6 @no_type_check 반영; python.rs line 429 어서션이 @no_type_check 포함 체크로 갱신 | | 5+6 | lang.rs: tests/test_foo.py 어서션 + NOT-stripped 문서화 | ✅ | line 108 테스트 케이스 추가, line 49-52 doc 주석 명확화 | ## Item (4) Push-back 평가 — ❌ 부분 반박 author 의 push-back 은 "TS/JS 클래스 메서드 데코레이터를 독립 unit 으로 방출하지 않는 것은 design intent" 라고 주장하며 HOTFIXES entry 2 (expression-level 함수) 와 typescript.rs 주석을 근거로 제시합니다. 이 주장 자체는 옳습니다. 그러나 **reviewer 가 지적한 문제의 차원을 잘못 파악했습니다**. 원래 우려는 "decorated class method 를 독립 unit 으로 방출하지 않는 것"이 아니라, 이미 방출되는 `method_definition` unit 의 `line_start` 가 `@decorator` 라인을 포함하지 않는다는 것입니다. 이는 Python 과의 cross-language 비일관성입니다. **Python**: `decorated_definition` 래퍼 노드가 존재하며 `walk()` 가 이를 만나면 outer node 의 start_position(= `@decorator` 라인)을 unit 의 line_start 로 사용합니다. 클래스 body 에서도 동일하게 `@classmethod def name()` unit 은 `@classmethod` 라인부터 시작합니다. **TypeScript/JavaScript**: `walk_class_body` 는 class_body 의 named_children 을 순회할 때 `decorator` 노드(kind="decorator")를 무시하고 지나칩니다. `method_definition` 을 만나면 `unit_start(&child)` 를 호출하는데, 이 함수는 prev_sibling 을 역방향 순회하며 `"comment"` 종류만 흡수하고 `"decorator"` 를 만나면 즉시 중단합니다. 결과적으로 `@Log\nmethod() {}` 에서 `@Log` 라인은 unit 의 line range 밖입니다. HOTFIXES entry 2 는 expression-level 함수(arrow fn / const assignment)에 관한 것이며, 클래스 메서드 데코레이터 라인 범위에 대한 항목은 존재하지 않습니다. 이 비일관성은 미기록 상태입니다. --- ## Round-2 신규 fresh pass 결과 - **snapshot naming** (`code-typescript-v1` / `code-javascript-v1` 잔류물): 없음 ✅ - **scaffold.rs visibility**: `pub(crate)` 적절, 외부 노출 불필요 ✅ - **module_path_for_python / _tsjs determinism**: 순수 문자열 연산, 비결정성 없음 ✅ - **TS/JS vs Python symbol separator 컨벤션 (/ vs .)**: design §3.4 표에 `TS/JS: module/Class.method` 로 명시, 의도적 선택 ✅ - **app dispatch try_skip_unchanged**: `ingest_one_code_asset` 에서 lang 분기 후 parser_version / chunker_version 을 per-lang 으로 올바르게 선택해 전달 ✅ - **HOTFIXES 날짜 형식**: `## 2026-05-20 —` 형식, 기존 엔트리와 일관 ✅ --- ## 결론 safety / correctness 관점에서 **1건** 의 실질적 문제 발견: `typescript.rs` + `javascript.rs` 의 `unit_start()` 가 `"decorator"` prev_sibling 을 흡수하지 않아 TS/JS 클래스 메서드 데코레이터 라인이 unit range 밖으로 떨어집니다. Python 의 동작과 비일관하며 HOTFIXES 에 미기록입니다. 아래 두 옵션 중 하나로 처리하면 됩니다: - **Option A (권장)**: `unit_start()` 의 prev_sibling 순회에서 `"decorator"` 도 `"comment"` 와 동일하게 흡수 (typescript.rs + javascript.rs 각 3줄 추가). Python 동작과 일치. - **Option B**: HOTFIXES 에 신규 항목 추가 후 defer — 비일관성이 영구화될 위험 있음. 나머지 round-1 수정 5건은 모두 검증됐습니다. 이 1건 처리 후 round 3 은 APPROVE 가능 예상입니다.
@@ -0,0 +243,4 @@
let sym = join_symbol(mod_prefix, mod_path, name);
units.push((sym, s, e, true));
}
}

[round-2, actionable — typescript.rs 와 동일 문제] walk_class_body 에서 unit_start(&child) 를 호출할 때 "decorator" prev_sibling 을 흡수하지 않아 @decorator method() 의 데코레이터 라인이 unit range 밖으로 떨어집니다. typescript.rs:248 의 동일한 문제 + 동일한 fix 를 적용하면 됩니다.

**[round-2, actionable — typescript.rs 와 동일 문제]** `walk_class_body` 에서 `unit_start(&child)` 를 호출할 때 `"decorator"` prev_sibling 을 흡수하지 않아 `@decorator method()` 의 데코레이터 라인이 unit range 밖으로 떨어집니다. typescript.rs:248 의 동일한 문제 + 동일한 fix 를 적용하면 됩니다.
@@ -0,0 +217,4 @@
fn unit_start(n: &tree_sitter::Node) -> u32 {
let mut start = n.start_position().row as u32 + 1;
let mut prev = n.prev_sibling();
while let Some(p) = prev {

[round-2, 참고] 주석에 "TS class / method decorators live INSIDE the parent declaration (as children, surfaced via the decorator field)" 라고 되어 있으나, tree-sitter-typescript 0.23 에서 클래스 메서드 데코레이터는 method_definition 의 자식 필드가 아닌 class_body 내 전행 형제(preceding sibling) 입니다. 이 부정확한 주석이 unit_start 에서 "decorator" 를 흡수하지 않은 근거가 된 것으로 보입니다. typescript.rs:248 의 fix 와 함께 주석도 실제 tree 구조를 ���영하도록 갱신 권장합니다.

**[round-2, 참고]** 주석에 "TS class / method decorators live INSIDE the parent declaration (as children, surfaced via the `decorator` field)" 라고 되어 있으나, tree-sitter-typescript 0.23 에서 클래스 **메서드** 데코레이터는 `method_definition` 의 자식 필드가 아닌 class_body 내 **전행 형제(preceding sibling)** 입니다. 이 부정확한 주석이 unit_start 에서 `"decorator"` 를 흡수하지 않은 근거가 된 것으로 보입니다. typescript.rs:248 의 fix 와 함께 주석도 실제 tree 구조를 ���영하도록 갱신 권장합니다.
@@ -0,0 +250,4 @@
let sym = join_symbol(mod_prefix, mod_path, name);
units.push((sym, s, e, true));
}
}

[round-2, actionable — 데코레이터 라인 범위 누락] unit_start(&child) 는 prev_sibling 을 역방향 순회할 때 "comment" 종류만 흡수하고 멈춥니다. tree-sitter-typescript 0.23 에서 @decorator method() {} 구조는 class_body 의 named_children 에서 decorator 노드가 method_definition전행 형제(prev_sibling) 로 위치합니다 — method_definition 의 자식 필드가 아닙니다. 따라서 unit_startprev_sibling.kind() == "decorator" 를 만나면 즉시 중단하고, @decorator 라인은 unit 의 line_start 에 놓입니다.

Python 은 decorated_definition 래퍼 덕분에 outer node 의 start_position(= @decorator 라인)을 unit_start 로 씁니다 — 클래스 메서드(@classmethod) 포함. 결과:

  • Python @classmethod def name() → unit line_start = @classmethod 라인
  • TS/JS @Log method() → unit line_start = method() 라인 (@Log 누락)

이 비일관성은 HOTFIXES 에 미기록이며, push-back 에서 인용된 HOTFIXES entry 2(expression-level 함수)와는 별개의 문제입니다.

권장 fix (typescript.rs + javascript.rs 동일, 각 3줄 변경):

if p.kind() == "comment" || p.kind() == "decorator" {
    start = p.start_position().row as u32 + 1;
    prev = p.prev_sibling();
} else {
    break;
}

fix 하지 않을 경우, HOTFIXES 에 Python 과의 비일관성을 신규 항목으로 기록해야 합니다.

**[round-2, actionable — 데코레이터 라인 범위 누락]** `unit_start(&child)` 는 prev_sibling 을 역방향 순회할 때 `"comment"` 종류만 흡수하고 멈춥니다. tree-sitter-typescript 0.23 에서 `@decorator method() {}` 구조는 class_body 의 named_children 에서 `decorator` 노드가 `method_definition` 의 **전행 형제(prev_sibling)** 로 위치합니다 — method_definition 의 자식 필드가 아닙니다. 따라서 `unit_start` 가 `prev_sibling.kind() == "decorator"` 를 만나면 즉시 중단하고, `@decorator` 라인은 unit 의 line_start **밖**에 놓입니다. Python 은 `decorated_definition` 래퍼 덕분에 outer node 의 start_position(= `@decorator` 라인)을 unit_start 로 씁니다 — 클래스 메서드(`@classmethod`) 포함. 결과: - Python `@classmethod def name()` → unit line_start = `@classmethod` 라인 ✅ - TS/JS `@Log method()` → unit line_start = `method()` 라인 (`@Log` 누락) ❌ 이 비일관성은 HOTFIXES 에 미기록이며, push-back 에서 인용된 HOTFIXES entry 2(expression-level 함수)와는 **별개의 문제**입니다. **권장 fix** (typescript.rs + javascript.rs 동일, 각 3줄 변경): ```rust if p.kind() == "comment" || p.kind() == "decorator" { start = p.start_position().row as u32 + 1; prev = p.prev_sibling(); } else { break; } ``` fix 하지 않을 경우, HOTFIXES 에 Python 과의 비일관성을 신규 항목으로 기록해야 합니다.
altair823 added 1 commit 2026-05-20 02:20:30 +00:00
Round 1 push-back on TS/JS class-method decorator handling was based on
an inaccurate doc comment in typescript.rs that claimed decorators are
method_definition children; tree-sitter-typescript 0.23 actually places
them as class_body preceding siblings. Round 2 correctly identified the
cross-language inconsistency with Python's decorated_definition arm.

Fix: extend unit_start backward walk in typescript.rs to also accept
'decorator' siblings (three-line change + corrected doc comment).
javascript.rs is unaffected: tree-sitter-javascript stores the decorator
as a named child INSIDE method_definition, so method_definition.start_row
already covers the decorator line without any sibling walk.

Adds three regression tests:
- class_method_decorator_folded_into_method_unit (TS): asserts @Log() is
  inside the emitted method unit code and line_start == 2.
- ts_class_decorator_folded_into_class_unit (TS): class-level @Injectable()
  folded into the class unit, line_start == 1.
- js_class_method_decorator_already_folded_by_grammar (JS): documents
  that JS already includes the decorator via grammar semantics.

verify: per-crate cargo test (20 passed) + clippy clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 approved these changes 2026-05-20 02:25:22 +00:00
claude-reviewer-01 left a comment
Member

회차 3 — 라운드 2 수정사항 전량 검증 완료. APPROVE.

라운드 2 수정 항목별 검증

1. TS unit_start 데코레이터 backward walk 확장

typescript.rs unit_start() 에서 p.kind() == "comment" || p.kind() == "decorator" 분기 추가 확인. cargo test -p kebab-parse-code class_method_decorator_folded_into_method_unitpass. 어서션: bar_block.code.contains("@Log()") + line_start == 2 — 의미 있는 검증.

2. JS 문법 비대칭 주장

javascript.rs unit_startcomment 분기만 가짐 — 라운드 2 전후 동일하게 unchanged. cargo test -p kebab-parse-code js_class_method_decorator_already_folded_by_grammarpass. tree-sitter-javascript 문법상 decoratormethod_definition 의 named child 이기 때문에 method_definition.start_row 가 이미 데코레이터 줄을 포함하는 동작이 실측으로 확인됨.

3. TS doc comment 수정

unit_start 함수 바로 위 doc comment 새 텍스트 확인:

"In tree-sitter-typescript 0.23, TS class-method decorators (and class-level decorators) are class_body siblings… (Contrast with tree-sitter-javascript, where the decorator IS stored inside method_definition as a named child via the decorator field…)"

TS-vs-JS 비대칭을 정확하게 서술. 구 문장 ("decorators live INSIDE the parent declaration…1B 1차 we do not specially unwrap them; this matches the plan §Task H note") 삭제 확인.

4. 회귀 테스트 3종

  • typescript::tests::class_method_decorator_folded_into_method_unit — TS, @Log() 메서드 데코레이터, line_start == 2
  • typescript::tests::ts_class_decorator_folded_into_class_unit — TS, @Injectable() 클래스 데코레이터, line_start == 1
  • javascript::tests::js_class_method_decorator_already_folded_by_grammar — JS, @Log() 문법 폴딩 기준선 문서화

전체 스위트: 34 passed (20 lib + 14 integration), 0 failed, clippy clean.

라운드 3 신규 점검

크로스 문서 일관성: tasks/HOTFIXES.md 에 데코레이터 관련 항목 없음 (PR 리뷰 수정이지 shipped deviation 아님). 구 부정확 doc comment 참조하는 task spec / plan 항목 없음. tasks/p10/p10-1b-py-ts-js-ast-chunkers.md 는 Python decorated_definition 만 언급 — 정확.

클래스-레벨 데코레이터 처리: ts_class_decorator_folded_into_class_unit 테스트 — @Injectable()program-scope sibling으로 unit_start backward walk 가 동일하게 적용, line_start == 1 pass.

trailing whitespace: 없음. 버전 / HOTFIXES 날짜: 0.8.0 / 2026-05-20 유지. eval flake: 재현 없음.

회차 3 — 라운드 2 수정사항 전량 검증 완료. **APPROVE.** ## 라운드 2 수정 항목별 검증 ### 1. TS `unit_start` 데코레이터 backward walk 확장 ✅ `typescript.rs` `unit_start()` 에서 `p.kind() == "comment" || p.kind() == "decorator"` 분기 추가 확인. `cargo test -p kebab-parse-code class_method_decorator_folded_into_method_unit` → **pass**. 어서션: `bar_block.code.contains("@Log()")` + `line_start == 2` — 의미 있는 검증. ### 2. JS 문법 비대칭 주장 ✅ `javascript.rs` `unit_start` 는 `comment` 분기만 가짐 — 라운드 2 전후 동일하게 **unchanged**. `cargo test -p kebab-parse-code js_class_method_decorator_already_folded_by_grammar` → **pass**. tree-sitter-javascript 문법상 `decorator` 가 `method_definition` 의 named child 이기 때문에 `method_definition.start_row` 가 이미 데코레이터 줄을 포함하는 동작이 실측으로 확인됨. ### 3. TS doc comment 수정 ✅ `unit_start` 함수 바로 위 doc comment 새 텍스트 확인: > "In tree-sitter-typescript 0.23, TS class-method decorators (and class-level decorators) are `class_body` siblings… (Contrast with tree-sitter-javascript, where the `decorator` IS stored inside `method_definition` as a named child via the `decorator` field…)" TS-vs-JS 비대칭을 정확하게 서술. 구 문장 ("decorators live INSIDE the parent declaration…1B 1차 we do not specially unwrap them; this matches the plan §Task H note") 삭제 확인. ### 4. 회귀 테스트 3종 ✅ - `typescript::tests::class_method_decorator_folded_into_method_unit` — TS, @Log() 메서드 데코레이터, `line_start == 2` - `typescript::tests::ts_class_decorator_folded_into_class_unit` — TS, @Injectable() 클래스 데코레이터, `line_start == 1` - `javascript::tests::js_class_method_decorator_already_folded_by_grammar` — JS, @Log() 문법 폴딩 기준선 문서화 전체 스위트: **34 passed (20 lib + 14 integration), 0 failed, clippy clean.** ## 라운드 3 신규 점검 **크로스 문서 일관성**: `tasks/HOTFIXES.md` 에 데코레이터 관련 항목 없음 (PR 리뷰 수정이지 shipped deviation 아님). 구 부정확 doc comment 참조하는 task spec / plan 항목 없음. `tasks/p10/p10-1b-py-ts-js-ast-chunkers.md` 는 Python `decorated_definition` 만 언급 — 정확. **클래스-레벨 데코레이터 처리**: `ts_class_decorator_folded_into_class_unit` 테스트 — `@Injectable()` 이 `program`-scope sibling으로 `unit_start` backward walk 가 동일하게 적용, `line_start == 1` pass. **trailing whitespace**: 없음. **버전 / HOTFIXES 날짜**: `0.8.0` / `2026-05-20` 유지. **eval flake**: 재현 없음.
@@ -0,0 +516,4 @@
}
}
/// In tree-sitter-javascript, `decorator` is a CHILD of

[칭찬] '수정 없이 통과'하는 동작을 회귀 테스트로 고정 — JS 문법에서 decoratormethod_definition 의 named child 임을 코드에 못 박아, 향후 grammar 업그레이드 시 동작 변화를 즉시 감지할 수 있는 가드레일이 됨.

**[칭찬]** '수정 없이 통과'하는 동작을 회귀 테스트로 고정 — JS 문법에서 `decorator` 가 `method_definition` 의 named child 임을 코드에 못 박아, 향후 grammar 업그레이드 시 동작 변화를 즉시 감지할 수 있는 가드레일이 됨.
@@ -0,0 +229,4 @@
let mut start = n.start_position().row as u32 + 1;
let mut prev = n.prev_sibling();
while let Some(p) = prev {
if p.kind() == "comment" || p.kind() == "decorator" {

[칭찬] || p.kind() == "decorator" 단 한 줄 추가로 TS 메서드/클래스 데코레이터를 모두 포함. 최소 변경으로 Python decorated_definition 언랩과의 대칭성을 회복한 깔끔한 수정.

**[칭찬]** `|| p.kind() == "decorator"` 단 한 줄 추가로 TS 메서드/클래스 데코레이터를 모두 포함. 최소 변경으로 Python `decorated_definition` 언랩과의 대칭성을 회복한 깔끔한 수정.
altair823 merged commit 1f566b8bfa into main 2026-05-20 02:26:28 +00:00
altair823 deleted branch feat/p10-1b-py-ts-js 2026-05-20 02:26:29 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: altair823-org/kebab#142