feat(p10-1B): Python + TS/JS AST chunkers — tree-sitter-{python,typescript,javascript} 코드 색인 활성화 #142
Reference in New Issue
Block a user
Delete Branch "feat/p10-1b-py-ts-js"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
개요
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로 확정)
kebab_eval/metrics.py의def compute_mrr()→ symbolkebab_eval.metrics.compute_mrr.pkg/__init__.py는pkg.module_path_for_python가 source of truth.src/Foo.ts의Foo.barmethod → symbolsrc/Foo.Foo.bar.export default function()→<prefix>.default.module_path_for_tsjs가 source of truth.tree_sitter_typescript::LANGUAGE_TYPESCRIPT(.ts) vsLANGUAGE_TSX(.tsx). 파일 확장자 기반 dispatch.tree_sitter_javascript::LANGUAGE가.js/.mjs/.cjs/.jsx모두 처리.const foo = () => {}는<top-level>glue 로 포함. HOTFIXES 2026-05-20 기록; 후속 phase 에서lexical_declarationunwrap 검토.ingest_one_code_asset(code_lang: &str)가 4-armmatch로 lang 별 Extractor + Chunker 선택. Rust 기존 동작 byte-identical 보존 (기존 통합 테스트로 검증).code-{rust,python,ts,js}-v1/code-{rust,python,ts,js}-ast-v1. design §3.3 표 일치.변경 요약
tree-sitter-python0.25,tree-sitter-typescript0.23,tree-sitter-javascript0.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_definitionunwrap (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_statementunwrap +valuefield 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-v1chunkers (1A-2code-rust-ast-v1near-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).sample.{py,ts,tsx,js}) + per-lang extractor 단위 테스트 + 3 per-lang chunker snapshot 통합 테스트 (in-memory CanonicalDocument로 chunk boundary §6.3 준수).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.Citation::Codesymbol/line 이 §3.4 컨벤션 일치.kebab ingest→ parser_versioncode-{python,ts,js}-v1+code_lang_breakdown에 3 lang 등장 +kebab search --code-lang {lang} --json→Citation::Code { lang, symbol }정상 반환.커밋 (16)
🤖 Generated with Claude Code
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>회차 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-v1chunker) 일관; 커밋31245a4수정 후 stalecode-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)
[missing-hotfix]
module_path_for_python/_tsjspath-sanitize 부재 — task spec Risks/notes 에서 HOTFIXES 기록을 약속했지만 실제 HOTFIXES 엔트리 없음. task specp10-1b-py-ts-js-ast-chunkers.mdRisks/notes: "HOTFIXES 에 path-sanitize 부재 기록" — HOTFIXES.md (2026-05-20) 에 해당 엔트리 없음. 세 번째 HOTFIX 엔트리 추가 필요 (비-ASCII / 공백 / 특수문자 workspace_path 미처리).[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 전 정리 권고.[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권고.[edge-case]
module_path_for_python이tests//examples/경로를 strip 하지 않음.tests/test_foo.py→ module prefixtests.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).[nit]
sample.pyfixture 에 module-level@staticmethod사용. Python 에서@staticmethod는 class method decorator — module-level 함수에 붙이면 syntactically valid 하지만 semantically 잘못된 코드. fixture 가 tree-sitter 처리 자체는 통과시키지만, 실제 코드에서는 발생하지 않는 패턴. 후속 reviewer 가 의아하게 볼 수 있으므로@cache/@functools.lru_cache같은 실제 쓰이는 decorator 로 교체 권고.[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-chunkno tree-sitter dep,kebab-appno grammar dep,kebab-parse-codeno store/embed).31245a4으로 TS 명명 수정(code-typescript-v1→code-ts-v1) 후 전체 소스/테스트/스냅샷/docs 에 stale string 없음 — 철저한 sweep.bigXxxoversize → 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-memoryBlock::Code직접 구성으로 경계를 지킨다.big_compute/bigTransform/BigProcessoroversize 분할까지 커버하고, 세 언어 모두 8 chunks 로 구조 일관.policy_hash_matches_md_heading_v1cross-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__.pyfold,src/strip,crates/*/src/strip,.pyiextension 등 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_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.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주석으로 명시 권고.@@ -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 로 교체가 더 적절하다.@@ -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.mdRisks/notes 에 "HOTFIXES 에 path-sanitize 부재 기록"이라고 명시했지만 이 파일에 해당 엔트리가 없다.module_path_for_python/module_path_for_tsjs가 비-ASCII / 공백 / 특수문자가 포함된 workspace_path 를 sanitize 없이 그대로 통과시키는 편차를 2026-05-20 세 번째 항목으로 추가해야 한다 — 다른 두 항목과 동일한 형식으로. spec 이 기록을 약속했으므로 이번 PR 에서 완결하는 것이 좋다.(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>회차 2 — 5개 round-1 수정 검증 완료, push-back 부분 반박, 신규 1건 (데코레이터 라인 범위 비일관성)
Round-1 수정 검증 결과 (5건)
cargo test -p kebab-parse-code17 passed 0 failedItem (4) Push-back 평가 — ❌ 부분 반박
author 의 push-back 은 "TS/JS 클래스 메서드 데코레이터를 독립 unit 으로 방출하지 않는 것은 design intent" 라고 주장하며 HOTFIXES entry 2 (expression-level 함수) 와 typescript.rs 주석을 근거로 제시합니다. 이 주장 자체는 옳습니다.
그러나 reviewer 가 지적한 문제의 차원을 잘못 파악했습니다. 원래 우려는 "decorated class method 를 독립 unit 으로 방출하지 않는 것"이 아니라, 이미 방출되는
method_definitionunit 의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 결과
code-typescript-v1/code-javascript-v1잔류물): 없음 ✅pub(crate)적절, 외부 노출 불필요 ✅TS/JS: module/Class.method로 명시, 의도적 선택 ✅ingest_one_code_asset에서 lang 분기 후 parser_version / chunker_version 을 per-lang 으로 올바르게 선택해 전달 ✅## 2026-05-20 —형식, 기존 엔트리와 일관 ✅결론
safety / correctness 관점에서 1건 의 실질적 문제 발견:
typescript.rs+javascript.rs의unit_start()가"decorator"prev_sibling 을 흡수하지 않아 TS/JS 클래스 메서드 데코레이터 라인이 unit range 밖으로 떨어집니다. Python 의 동작과 비일관하며 HOTFIXES 에 미기록입니다. 아래 두 옵션 중 하나로 처리하면 됩니다:unit_start()의 prev_sibling 순회에서"decorator"도"comment"와 동일하게 흡수 (typescript.rs + javascript.rs 각 3줄 추가). Python 동작과 일치.나머지 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 를 적용하면 됩니다.@@ -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
decoratorfield)" 라고 되어 있으나, 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_start가prev_sibling.kind() == "decorator"를 만나면 즉시 중단하고,@decorator라인은 unit 의 line_start 밖에 놓입니다.Python 은
decorated_definition래퍼 덕분에 outer node 의 start_position(=@decorator라인)을 unit_start 로 씁니다 — 클래스 메서드(@classmethod) 포함. 결과:@classmethod def name()→ unit line_start =@classmethod라인 ✅@Log method()→ unit line_start =method()라인 (@Log누락) ❌이 비일관성은 HOTFIXES 에 미기록이며, push-back 에서 인용된 HOTFIXES entry 2(expression-level 함수)와는 별개의 문제입니다.
권장 fix (typescript.rs + javascript.rs 동일, 각 3줄 변경):
fix 하지 않을 경우, HOTFIXES 에 Python 과의 비일관성을 신규 항목으로 기록해야 합니다.
회차 3 — 라운드 2 수정사항 전량 검증 완료. APPROVE.
라운드 2 수정 항목별 검증
1. TS
unit_start데코레이터 backward walk 확장 ✅typescript.rsunit_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.rsunit_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 새 텍스트 확인: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 == 2typescript::tests::ts_class_decorator_folded_into_class_unit— TS, @Injectable() 클래스 데코레이터,line_start == 1javascript::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는 Pythondecorated_definition만 언급 — 정확.클래스-레벨 데코레이터 처리:
ts_class_decorator_folded_into_class_unit테스트 —@Injectable()이program-scope sibling으로unit_startbackward walk 가 동일하게 적용,line_start == 1pass.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 문법에서
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 메서드/클래스 데코레이터를 모두 포함. 최소 변경으로 Pythondecorated_definition언랩과의 대칭성을 회복한 깔끔한 수정.