[버그] patchHwpxBlocks — whitespace-only 빈 셀 채움 시 기존 character-data가 제거됨
### 요약
`patchHwpxBlocks`로 IR상 빈 표 셀에 값을 채울 때, raw text node가 XML whitespace만 포함하면 patch는 성공으로 보고되지만 기존 character-data가 제거됩니다. 이는 요청한 삽입 경계 밖의 원문을 변경합니다.
### 환경
- `kordoc@4.2.4` exact npm package
- Node.js `v26.5.0`
- macOS arm64
- 합성 HWPX만 사용하며 실제 문서나 개인정보는 포함하지 않음
### 재현
아래 공개 합성 테스트는 프로젝트 의존성과 설치된 runtime 버전의 exact 일치를 먼저 검사합니다.
- `reproductions/kordoc-4.0.1-whitespace-only-cell-preservation.test.mjs`
- 실행: `node --test reproductions/kordoc-4.0.1-whitespace-only-cell-preservation.test.mjs`
테스트 파일명은 최초 발견 버전을 나타내지만, exact `4.2.0`과 `4.2.4`에서 같은 결과를 재현했습니다.
### 관측 결과
- patch `success/applied/skipped`: `true/1/0`
- 입력 IR cell text length: `0`
- 입력 IR cell text SHA-256: `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
- 기존 outside-boundary character count: `1`
- 해당 character SHA-256: `36a9e7f1c95b82ffb99743e0c5c4ce95d83c9a430aac59f84ef3cbfab6145068`
- approved replacement occurrence after patch: `1`
- after prefix/suffix character count: `0/0`
- stable assertion: `KORDOC_WHITESPACE_ONLY_CELL_OUTSIDE_TARGET_REMOVED`
즉 값 자체는 정확히 한 번 들어가지만, 기존 outside-boundary character가 `1 -> 0`으로 사라집니다.
### 원인 경계
`src/roundtrip/source-map.ts`의 `buildParagraphSplices`는 원문 전체가 non-whitespace일 때만 leading/trailing whitespace 복원 분기로 진입합니다. whitespace-only 문단은 첫 text range 전체교체 경로로 들어가므로 기존 character-data가 제거됩니다.
단순히 조건만 완화하면 whitespace-only 문자열 전체가 leading과 trailing에 동시에 해당하여 삽입 방향이 모호합니다. 반면 현재 공개된 `buildRangeSplices`를 같은 문단의 t-domain `[0,0)`에 적용한 합성 측정은 다음을 보였습니다.
- range splice count: `1`
- approved replacement occurrence: `1`
- after prefix/suffix character count: `0/1`
- suffix SHA-256: 기존 outside-boundary character와 동일
### 기대 동작
IR상 빈 셀에 값을 채우는 경로는 raw whitespace를 교체 대상으로 추론하지 말고, 정확한 zero-length insertion range를 사용해 기존 character-data를 보존해야 합니다. API가 삽입 위치를 유일하게 결정할 수 없다면 성공으로 보고하지 않고 fail-closed해야 합니다.
### 최소 수정 방향 제안
`applyCellEdit`의 empty-cell 분기에서 raw t-domain이 whitespace-only이고 단일 삽입 경계를 안전하게 선택할 수 있는 경우, 문단 전체를 `buildParagraphSplices`로 교체하는 대신 `buildRangeSplices(target, xml, 0, 0, replacement)`를 사용합니다.
다음 회귀 테스트를 권장합니다.
1. whitespace-only 단일 text range: 기존 character 보존
2. whitespace 여러 문자: 전부 suffix로 보존
3. self-closing text/run: 기존 삽입 동작 무회귀
4. 복수 text range 또는 markup으로 삽입 경계가 모호한 경우: silent success 금지
5. non-whitespace empty-cell 오판 방지
호출자 측 raw XML 수정이나 verifier 완화 없이 Kordoc 공식 patch 경로에서 해결되기를 기대합니다.
issue