Skip to content

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

Merged
jk-kim0 merged 6 commits intomainfrom
jk/feat-sidecar-type-based-core
Mar 12, 2026
Merged

confluence-mdx: sidecar mapping을 텍스트 비교에서 타입 기반 순차 정렬로 교체합니다#901
jk-kim0 merged 6 commits intomainfrom
jk/feat-sidecar-type-based-core

Conversation

@jk-kim0
Copy link
Contributor

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

배경

generate_sidecar_mapping()이 XHTML 블록과 MDX 블록을 매칭할 때 텍스트 유사도 비교를 사용했습니다.
이로 인해 두 가지 문제가 있었습니다:

  1. 매칭 정확도 낮음 — emoticon, lost_info 등 텍스트 차이로 오매칭 발생
  2. 폴백 체인 누적 — 매칭 실패를 보완하기 위해 _resolve_child_mapping() 4단계 폴백과 _find_containing_mapping() 텍스트 포함 검색이 추가됨

변경 내용

1. sidecar.py: 타입 기반 two-pointer 매칭 (mapping.yaml v3)

_TYPE_COMPAT 테이블로 XHTML/MDX 블록 타입 호환성을 선언하고 두 포인터를 순차 진행하며 매칭합니다.
텍스트 비교를 일절 사용하지 않습니다.

sidecar 엔트리에 다음 필드를 추가합니다:

  • xhtml_type: XHTML 블록의 타입
  • mdx_line_start / mdx_line_end: 대응 MDX 블록의 줄 번호
  • children: compound 블록(callout 등)의 자식 정렬 정보

타입 불일치 시 XHTML 블록에 MDX 대응 없음으로 처리합니다 (ac:image, toc 등 forward converter가 MDX로 출력하지 않는 블록).

2. list_patcher.py: 항목 변경 시 항상 전체 리스트 재생성

_resolve_child_mapping() (4단계 텍스트 폴백)을 삭제합니다.
변경된 항목이 하나라도 있으면 전체 리스트 inner XHTML을 재생성합니다.

3. patch_builder.py: 텍스트 포함 검색 제거

_find_containing_mapping() (텍스트 포함 검색)과 _strip_block_markers()를 삭제합니다.
_resolve_mapping_for_change()에서 다음 규칙을 명시적으로 선언합니다:

  • callout → 항상 containing 전략
  • children 있는 mapping → containing 또는 list 전략
  • sidecar 조회 실패 → skip

삭제된 코드

함수 줄 수 이유
_resolve_child_mapping() ~70줄 타입 기반 sidecar로 불필요
_find_containing_mapping() ~25줄 sidecar O(1) 조회로 대체
_strip_block_markers() ~5줄 _find_containing_mapping 삭제로 불필요

Added/updated tests?

  • Yes — test_reverse_sync_sidecar.py: 타입 기반 매핑 단위 테스트
  • Yes — test_reverse_sync_patch_builder.py: 단순화된 전략 결정 테스트
  • Yes — test_reverse_sync_cli.py: v3 스키마 E2E 테스트
  • Yes — testcase expected 파일 갱신

@vercel
Copy link

vercel bot commented Mar 11, 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 12, 2026 2:45pm

Request Review

@jk-kim0 jk-kim0 force-pushed the jk/feat-sidecar-type-based-core branch from 9b6263c to d9f7856 Compare March 12, 2026 07:02
jk-kim0 added a commit that referenced this pull request Mar 12, 2026
list_patcher의 _regenerate_list_from_parent에서 parent XHTML에 <ac:image>가 있을 때
text_transfer로만 폴백하던 로직을 개선합니다.

변경 내용:
- _regenerate_preserving_lossy_items 함수 추가: top-level <li> 항목 수가 같을 때
  선택적 재생성을 시도합니다.
  - lossy 요소(ac:image, span style)가 없는 항목은 mdx_block_to_inner_xhtml로 재생성
    → inline bold/italic 경계 변경이 올바르게 적용됨
  - lossy 요소가 있는 항목은 텍스트 변경이 없으면 old XHTML에서 그대로 보존
  - lossy 항목의 텍스트도 변경된 경우 None 반환 → text_transfer 폴백 유지
- 테스트 추가: test_reverse_sync_list_patcher_image_preserve.py
- tests/run-tests.sh: --xhtml → --page-dir 수정 (PR #901 CLI 변경 대응)
- pages.yaml: 544377652 expected_status fail → pass 갱신

수정된 버그:
- failure_type 13: **Scopes**: → **Scopes: 텍스트가 Bold 안으로 흡수 (544377652)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jk-kim0 added a commit that referenced this pull request Mar 12, 2026
list_patcher의 _regenerate_list_from_parent에서 parent XHTML에 <ac:image>가 있을 때
text_transfer로만 폴백하던 로직을 개선합니다.

변경 내용:
- _regenerate_preserving_lossy_items 함수 추가: top-level <li> 항목 수가 같을 때
  선택적 재생성을 시도합니다.
  - lossy 요소(ac:image, span style)가 없는 항목은 mdx_block_to_inner_xhtml로 재생성
    → inline bold/italic 경계 변경이 올바르게 적용됨
  - lossy 요소가 있는 항목은 텍스트 변경이 없으면 old XHTML에서 그대로 보존
  - lossy 항목의 텍스트도 변경된 경우 None 반환 → text_transfer 폴백 유지
- 테스트 추가: test_reverse_sync_list_patcher_image_preserve.py
- tests/run-tests.sh: --xhtml → --page-dir 수정 (PR #901 CLI 변경 대응)
- pages.yaml: 544377652 expected_status fail → pass 갱신

수정된 버그:
- failure_type 13: **Scopes**: → **Scopes: 텍스트가 Bold 안으로 흡수 (544377652)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jk-kim0 jk-kim0 force-pushed the jk/feat-sidecar-type-based-core branch from d9f7856 to f21823b Compare March 12, 2026 10:42
@jk-kim0 jk-kim0 force-pushed the jk/feat-sidecar-type-based-core branch from f21823b to 3941948 Compare March 12, 2026 11:48
@jk-kim0 jk-kim0 self-assigned this Mar 12, 2026
jk-kim0 and others added 6 commits March 12, 2026 23:41
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>
- 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>
- `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

- feature/type-based-sidecar-mapping

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- roundtrip_verifier: _normalize_blank_line_after_blockquote 삭제
  - forward converter 가 blockquote 이후 빈 줄을 항상 추가하므로
  - improved.mdx 에서도 동일하게 요구하는 것이 올바름
  - 실제 mismatch 를 마스킹하는 이 정규화를 제거합니다
- 1907294209/improved.mdx: blockquote 이후 빈 줄 추가
  - 라인 19, 110 에 빈 줄 삽입하여 forward converter 출력과 일치하도록 수정합니다
- pages.yaml: 1297383451, 1907294209 expected_status fail → pass
  - sidecar type-based 매핑 개선으로 실제 통과하는 테스트 케이스이므로
  - expected_status 를 pass 로 업데이트합니다

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jk-kim0 jk-kim0 force-pushed the jk/feat-sidecar-type-based-core branch from 710a8e6 to 55bd453 Compare March 12, 2026 14:41
@jk-kim0 jk-kim0 merged commit 1597a43 into main Mar 12, 2026
7 checks passed
@jk-kim0 jk-kim0 deleted the jk/feat-sidecar-type-based-core branch March 12, 2026 16:05
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