Skip to content

confluence-mdx: sidecar mapping을 텍스트 비교에서 타입 기반 순차 정렬로 교체합니다#895

Draft
jk-kim0 wants to merge 14 commits intomainfrom
feature/type-based-sidecar-mapping
Draft

confluence-mdx: sidecar mapping을 텍스트 비교에서 타입 기반 순차 정렬로 교체합니다#895
jk-kim0 wants to merge 14 commits intomainfrom
feature/type-based-sidecar-mapping

Conversation

@jk-kim0
Copy link
Contributor

@jk-kim0 jk-kim0 commented Mar 9, 2026

배경

generate_sidecar_mapping()이 XHTML 블록과 MDX 블록을 매칭할 때
텍스트 유사도 비교를 사용해 정확도가 낮고 유지보수가 어려웠습니다.

변경 내용

mapping.yaml v3 스키마

각 매핑 엔트리에 다음 필드 추가:

  • xhtml_type: XHTML 블록의 타입
  • mdx_line_start / mdx_line_end: 대응하는 MDX 블록의 줄 번호
  • children: 자식 블록 정렬 정보 (xhtml_xpath, xhtml_block_id, mdx_line_start, mdx_line_end)

타입 기반 two-pointer 알고리즘

_TYPE_COMPAT 테이블로 XHTML/MDX 블록 타입 호환성을 선언하고,
두 포인터(XHTML 순서 vs MDX 블록 순서)를 순차적으로 진행하며 매칭합니다.

heading  ↔ heading
paragraph ↔ paragraph
list      ↔ list
code      ↔ code_block
table     ↔ table | html_block
html_block ↔ callout | details | figure | badge | hr | ...

특수 케이스 처리

  • _SKIP_MACROS: TOC/children 매크로는 MDX 블록 없음으로 처리
  • 빈 단락(paragraph) XHTML 블록: MDX 콘텐츠 없음으로 처리
  • _align_children(): 자식 블록도 타입 기반으로 정렬

검증 계획

  • 765개 pytest 단위 테스트 통과
  • reverse-sync integration test 16/16 통과

현재 상태

  • pytest 단위 테스트: 765/765 통과
  • integration test: 계속 진행 중 (12/16 → 16/16 목표)

🤖 Generated with Claude Code

@jk-kim0 jk-kim0 self-assigned this Mar 9, 2026
@vercel
Copy link

vercel bot commented Mar 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
querypie-docs Ready Ready Preview, Comment Mar 11, 2026 11:12am

Request Review

@jk-kim0 jk-kim0 force-pushed the feature/type-based-sidecar-mapping branch from 638bfd2 to b1e1514 Compare March 9, 2026 17:09
jk-kim0 added a commit that referenced this pull request Mar 10, 2026
PR #895 의 tests/reverse-sync/ 변경사항을 모두 취소합니다.
main 브랜치(PR #896, #897)의 테스트케이스가 올바른 기준입니다.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jk-kim0 jk-kim0 force-pushed the feature/type-based-sidecar-mapping branch from c192fbc to 03dfd58 Compare March 10, 2026 02:59
@jk-kim0 jk-kim0 force-pushed the feature/type-based-sidecar-mapping branch from 03dfd58 to a656ad5 Compare March 10, 2026 04:30
@jk-kim0 jk-kim0 force-pushed the feature/type-based-sidecar-mapping branch from a656ad5 to b0fc47a Compare March 10, 2026 04:39
@jk-kim0 jk-kim0 force-pushed the feature/type-based-sidecar-mapping branch 2 times, most recently from 16ec541 to 76a3fba Compare March 10, 2026 06:30
@jk-kim0 jk-kim0 force-pushed the feature/type-based-sidecar-mapping branch from 7302033 to f7e30b2 Compare March 11, 2026 07:34
jk-kim0 and others added 3 commits March 11, 2026 19:14
mapping.yaml v3 스키마를 도입하고, generate_sidecar_mapping()을
텍스트 유사도 매칭 대신 XHTML 블록 타입과 MDX 블록 타입의
호환성(_TYPE_COMPAT 테이블)을 기반으로 한 two-pointer 순차 정렬로
교체합니다.

주요 변경 사항:
- _TYPE_COMPAT: XHTML → MDX 블록 타입 호환성 매핑 테이블 추가
- _SKIP_MACROS: TOC/children 매크로 스킵 처리 추가
- SidecarChildEntry 데이터클래스: 자식 블록 정렬 정보 저장
- SidecarEntry: mdx_line_start, mdx_line_end, children 필드 추가
- generate_sidecar_mapping(): 타입 기반 두 포인터 알고리즘으로 재작성
- load_sidecar_mapping(): v3 스키마 필드 읽기 지원 추가
- 빈 단락(paragraph) XHTML 블록은 MDX 콘텐츠 블록 없음으로 처리
- 테스트 업데이트: version 2→3, Callout MDX 형식, mdx_line_start 검증

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
generate_sidecar_mapping()에서 MDX의 첫 번째 H1 헤딩(# 로 시작)은
Confluence XHTML 본문에 대응하지 않는 페이지 제목이므로, two-pointer
루프 시작 전에 건너뛰도록 예외 처리합니다.

이로 인해 사이드카 정렬이 모든 케이스에서 올바르게 동작하며,
integration test 16/16 통과 (기존 12/16 → 16/16).

544112828 expected 파일을 새 알고리즘 출력으로 갱신합니다.
(verification exact_match: true, 기존 expected는 구버전 알고리즘 기준)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- reverse_sync_cli.py: SidecarEntry 생성 시 children(SidecarChildEntry) 로드 추가
- patch_builder.py: _resolve_mapping_for_change() 단순화
  - callout 블록 조기 반환(containing 전략) 추가
  - mapping is None 시 _find_containing_mapping() 폴백 제거
  - 텍스트 불일치 시 _find_containing_mapping() 재매핑 제거
  - _resolve_child_mapping() import 복원(Phase 3 삭제 예정)
- list_patcher.py: build_list_item_patches()에서 _find_containing_mapping() 폴백 제거
- 테스트: 텍스트 폴백 제거에 따른 기대값 업데이트(skip/0 patches)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jk-kim0 and others added 7 commits March 11, 2026 19:14
- sidecar.py: _find_text_match(), _count_child_mdx_blocks(), _strip_all_ws() 삭제
- patch_builder.py: _find_containing_mapping(), _strip_block_markers() 삭제
  - _resolve_mapping_for_change() 서명에서 id_to_mapping 파라미터 제거
  - mapping.children 존재 시 containing 전략으로 단순화
- list_patcher.py: _resolve_child_mapping() 삭제
  - build_list_item_patches() 단순화: 항목 변경 시 전체 리스트 재생성
  - 미사용 import 제거 (collapse_ws, convert_inline, strip_list_marker, strip_for_compare)
- 테스트: 삭제된 함수 테스트 클래스 제거 및 기대값 업데이트

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…딩 공백 처리

## Description
- `bin/mdx_to_storage/inline.py`, `bin/reverse_sync/mdx_to_xhtml_inline.py`: inline code 변환 시 `html.escape()` 적용
  - `<`, `>`, `&` 등 HTML 특수문자가 포함된 코드 스니펫이 XHTML에서 깨지는 문제를 수정합니다.
- `bin/reverse_sync/list_patcher.py`, `bin/reverse_sync/xhtml_patcher.py`: `<ol start="N">` 속성 변경 감지 및 적용
  - `_get_ordered_list_start()` 헬퍼를 추가하여 MDX 순서 목록의 시작 번호를 추출합니다.
  - 시작 번호가 변경된 경우 패치에 `ol_start` 필드를 포함하고, xhtml_patcher에서 `<ol start>` 속성을 갱신하거나 제거합니다.
- `bin/reverse_sync/patch_builder.py`: 헤딩 공백 차이만 있는 변경을 XHTML에 전파하지 않음
  - 레벨과 텍스트가 동일한 헤딩 변경(공백 차이만 있는 경우)은 건너뜁니다.
  - Confluence XHTML의 `&nbsp;` 등 고유 공백 문자가 손실되는 것을 방지합니다.
- `bin/text_utils.py`: HTML 태그 제거로 인한 trailing space 아티팩트 제거
  - 링크 텍스트의 genuine trailing space는 보존하고, 태그 제거로 생긴 공백만 제거합니다.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## Description
- `tests/test_reverse_sync_mdx_to_xhtml_inline.py`: inline code 내 HTML 특수문자 escape 테스트 추가
  - `TestConvertInline`: `<`, `&` 포함 코드 스니펫의 escape 처리 검증
  - `TestBlockConversion`: reverse-sync 경로(`mdx_block_to_inner_xhtml`)에서의 escape 처리 검증
- `tests/test_reverse_sync_xhtml_patcher.py`: `<ol start="N">` 속성 변경 테스트 추가
  - `ol_start` 패치 필드로 `start` 속성을 설정하는 케이스
  - `ol_start=1` 패치로 `start` 속성을 제거하는 케이스
- `tests/test_reverse_sync_patch_builder.py`: `TestHeadingWhitespaceSkip` 클래스 추가
  - 공백 차이만 있는 헤딩 변경이 패치를 생성하지 않는 것 검증
  - 실제 텍스트가 변경된 헤딩은 패치를 생성하는 것 검증
- `bin/text_utils.py`: trailing space 아티팩트 제거 로직 revert
  - `s_before_html` 비교를 통한 trailing space 제거는 특정 케이스만을 위한 해킹으로 판단하여 취소합니다.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Forward converter의 체계적 차이(trailing whitespace, 이중 공백, <br/> 앞 공백)를
항상 정규화하여 검증 신뢰성을 높입니다.

## Description
- `roundtrip_verifier.py`에 항상 적용하는 최소 정규화(`_apply_minimal_normalizations`)를 추가합니다:
  - `_normalize_consecutive_spaces_in_text`: 코드 블록 외 텍스트에서 이중 공백을 단일 공백으로 정규화
  - `_normalize_br_space`: `<br/>` 앞의 불필요한 공백 제거
- `text_utils.py`의 `normalize_mdx_to_plain`에서 태그 제거 후 trailing space를 제거합니다.
- 이 변경으로 6개의 기존 실패 케이스가 PASS로 전환됩니다.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Forward converter 특성에 의한 체계적 차이를 최소 정규화에 추가합니다.

## Description
- `_normalize_table_cell_padding`을 `_apply_minimal_normalizations`으로 이동합니다:
  - 셀 내 연속 공백을 단일 공백으로 정규화
  - 구분자 행(`| --- |`)의 대시 수를 3개로 정규화 (컬럼 폭 차이 무시)
- `_strip_first_heading` + `lstrip('\n')` 추가: Forward converter가 H1을 페이지 제목으로 처리하므로 비교에서 제외합니다.
- `_normalize_trailing_blank_lines` 추가: 후미 빈 줄을 정규화합니다.
- `tests/reverse-sync/544178422/improved.mdx`: alt 텍스트 오타 수정 (`접근 차단` → `접근차단`)
- 이 변경으로 5개 케이스 추가 PASS: 1456144391, 1495433217, 544376183, 873136365, 544178422

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## Description
- `reverse_sync_cli.py`: 패치된 XHTML의 forward 변환 전, `page.v1.yaml`을
  `var/<page_id>/`로 복사합니다. Forward converter가 크로스 페이지 링크 해석 시
  `page.v1.yaml`을 XHTML과 같은 디렉터리에서 읽으므로, 이를 보장합니다.
- `roundtrip_verifier.py`: `_normalize_blank_line_after_blockquote()` 추가 —
  blockquote(`>`) 줄 바로 다음의 단일 빈 줄을 제거합니다. Forward converter가
  blockquote 뒤에 빈 줄을 체계적으로 추가하므로, 비교 시 이를 정규화합니다.

### 효과
- 1454342158: `#unexpected-failure` 링크 → 정상 크로스 페이지 링크로 수정
- 1907294209: blockquote 뒤 빈 줄 차이 제거 → PASS

## Related tickets & links
- feature/type-based-sidecar-mapping

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…드 전용으로 이동합니다

- _normalize_trailing_ws를 _apply_minimal_normalizations에서 제거합니다
- _apply_normalizations(lenient 전용)에 _normalize_trailing_ws를 추가합니다
- strict 모드에서 trailing whitespace 차이가 실패하도록 수정합니다

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jk-kim0 and others added 4 commits March 11, 2026 20:08
1. `bin/text_utils.py`: `normalize_mdx_to_plain`에서 HTML 태그 제거로
   생긴 trailing space 아티팩트를 제거합니다. (링크 텍스트의 genuine
   trailing space는 보존)

2. `bin/reverse_sync/patch_builder.py`: heading 블록에서 레벨과 텍스트가
   동일한 경우(공백 차이만 있는 경우) XHTML을 변경하지 않습니다.
   (`&nbsp;` 등 XHTML 고유 공백 문자가 손실되는 것을 방지)

3. `tests/reverse-sync/*/improved.mdx`: WS_ONLY 테스트케이스 11건
   trailing space, double space 등 whitespace 정규화

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
각 테스트케이스의 page.xhtml을 현재 버전의 forward converter로 변환하여
original.mdx와 mapping.yaml을 업데이트합니다.

이를 통해 테스트케이스가 현재 converter 버전의 출력을 정확하게 반영하게 됩니다.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- tests/reverse-sync/pages.yaml: 25개 미등록 테스트케이스 항목을 var/pages.yaml 에서 복사하여 추가합니다
- tests/reverse-sync/*/original.mdx: 전체 42개 케이스를 올바른 --attachment-dir 옵션으로 재생성합니다
  - attachment-dir 는 pages.yaml 의 mdx_path 에서 유도하여 이미지 경로가 실제 문서 경로와 일치하게 합니다
- tests/reverse-sync/*/mapping.yaml: original.mdx 재생성에 따른 sidecar 매핑 업데이트
- tests/reverse-sync/README.md: page.xhtml → original.mdx 변환 가이드 문서 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 543981760: improved.mdx의 HTML 테이블을 original.mdx 형식으로 복원합니다
- 544382364: <br/> 주변 blank line 누락 및 셀 내용 형식을 수정합니다
- 793608206: paragraph 텍스트 변경으로 round-trip 검증 가능하도록 수정합니다
- bin: list_patcher, xhtml_patcher, mdx_to_xhtml_inline, inline 개선사항 포함
- tests: page.v1.yaml 및 mapping.yaml 업데이트합니다

Co-Authored-By: Atlas <atlas@jk.agent>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant