diff --git a/resources/tester/test_est_text_units.py b/resources/tester/test_est_text_units.py
index 2293fa04d..20cb36b34 100644
--- a/resources/tester/test_est_text_units.py
+++ b/resources/tester/test_est_text_units.py
@@ -3,6 +3,30 @@
from test_est_rea_parity import run
+def test_replace_cost_words_preserves_markers_and_checks_lengths_before_changes():
+ result = run(
+ "math",
+ r"""(()=>{
+ const rows=[{index:3,row:{LineData:'\x0e모래\x0e + 모래 = \x121\x12'}},{index:5,row:{LineData:'유지'}}];
+ const before=JSON.stringify(rows),changed=EstText.replaceWords(rows,'모래','자갈');let rejected=0;
+ for(const [a,b] of [['','x'],['모래','\x12'],['모래','x'.repeat(200001)]]){
+ try{EstText.replaceWords(rows,a,b)}catch{rejected++}
+ }
+ const markers=EstText.replaceWords([{index:7,row:{LineData:'Q=1 = \x121\x12; cost \x031\x03'}}],'1','2');
+ const calcRows=[{Index:1,Code:'H',LineData:'head'},{LineData:'Q=1 = \x121\x12'}];
+ const update=EstText.replaceWords([{row:calcRows[1],index:1}],'1','2')[0];calcRows[1].LineData=update.value;
+ const recalculated=EstText.calculate(calcRows);
+ return {changed,rejected,markers,unchanged:JSON.stringify(rows)===before,
+ crossing:EstText.replaceWords(rows,'모래 +','x').length,recalculated:recalculated.rows[1].LineData,errors:recalculated.errors};
+ })()""",
+ )
+ assert result["changed"] == [{"index": 3, "value": "\x0e자갈\x0e + 자갈 = \x121\x12"}]
+ assert result["rejected"] == 3
+ assert result["markers"] == [{"index": 7, "value": "Q=2 = \x121\x12; cost \x031\x03"}]
+ assert "\x122\x12" in result["recalculated"] and not result["errors"]
+ assert result["unchanged"] and result["crossing"] == 0
+
+
def test_text_units_and_description_columns_preserve_numeric_assignments():
result = run(
"math",
diff --git a/tools/est-test/PLAN.md b/tools/est-test/PLAN.md
index 1f42c0e20..4e21fd277 100644
--- a/tools/est-test/PLAN.md
+++ b/tools/est-test/PLAN.md
@@ -1,4 +1,6 @@
## Codex · EST 독립 테스트 앱 (2026-11-04)
+- [x] 산출근거 문구 일괄 바꾸기 → 현재 호표·전체 범위, 표지 보존·재계산·실행 취소 확인.
+ - 자체 검증: Tailscale HTTP Edge에서 현재 호표·전체 치환, 일치 없음 안내, 결과·단가 표지 보존과 실행 취소 확인. 산출근거 시험 24개 통과, 수식 변경 후 재계산 확인.
- [x] Excel 파일 읽는 동안 파일·셀 선택이 바뀌면 이전 요청 반영 차단 → 지연 응답 시험.
- 자체 검증: 지연된 Tailscale HTTP 응답 동안 셀과 파일을 각각 변경하면 이전 요청 반영이 차단되고 다시 실행은 성공. 수량 시험 12개 통과.
- 자체 검증: 수량·Excel·설계변경·기성·물가변동·연차·문서·오류 기능 회귀 69개 통과(openpyxl 경고 8건). 원본 동등성 대조는 실행하지 않음.
diff --git a/tools/est-test/README.md b/tools/est-test/README.md
index 109728e24..d25ba0dbc 100644
--- a/tools/est-test/README.md
+++ b/tools/est-test/README.md
@@ -14,6 +14,8 @@
행을 선택한 뒤 편집 메뉴의 `선택 행 자릿수 설정`에서 처리방식과 소수 자릿수를 일괄 적용할 수 있습니다. 기존 계산 설정은 실행 취소로 되돌릴 수 있습니다.
+단가산출 화면의 `편집 → 산출근거 문구 바꾸기`는 현재 호표 또는 전체 산출근거에서 문구를 일괄 변경합니다. 결과값·연결 단가 등의 계산 표지는 치환하지 않으며, 자동계산이 켜져 있으면 변경 후 재계산합니다. 실행 취소로 복원할 수 있습니다.
+
내역서 편집 메뉴의 `내역 수량 초기화`는 선택한 상세 행 또는 모든 상세 행의 수량을 0으로 바꿉니다. 비율·합계 자동행은 건드리지 않으며 실행 취소할 수 있습니다.
인쇄 미리보기는 화면의 100행 페이지 제한 없이 검색 결과/현재 호표의 전체 행을 표시합니다. 출력 범위에서 현재 자료의 모든 호표·행도 선택할 수 있습니다. 원본 인쇄 옵션의 41개 항목 중 CAD를 제외한 40개 서식 이름을 선택하며, ESTX에서 가져온 해당 프로젝트의 인쇄 설정을 우선 사용합니다. 내역·원가·호표/총괄·증감·동원·수량·공정은 표 자료로 출력합니다. 표지·갑지·설명서·시방서·심사 문서는 서식별 본문을 직접 입력합니다. 용지·방향·여백·글자 크기·페이지당 행·시작 쪽·업체명을 저장하고 인쇄/PDF로 출력합니다. ESTX 수정 저장은 서식별 여백과 페이지당 행 수를 반영합니다. 다른 웹 인쇄 설정의 ESTX 쓰기와 원본 인쇄 도형·병합·글꼴 배치의 동등성은 지원하지 않습니다.
diff --git a/tools/est-test/index.html b/tools/est-test/index.html
index 5500d22bb..93aa61582 100644
--- a/tools/est-test/index.html
+++ b/tools/est-test/index.html
@@ -75,6 +75,7 @@
>