[논문 리뷰] Agentic Method for Deterministic Validation of Legacy Code Migration


메타정보

항목 내용
저자 Andras Ferenczi et al. (저자 5명, American Express)
학회 arXiv preprint · 2026
arXiv 또는 DOI 2607.28271
데이터 AWS CardDemo CBACT01C (430 lines) · CBSTM03A (924 lines) · 사내 production-like 배치 프로그램 (4,114 lines)
리뷰 일자 2026-08-21

TL;DR

  • COBOL 을 Java 로 1:1 이관할 때 진짜 어려운 건 번역이 아니라 검증이다. 실제 테스트 데이터는 없고, 수십 년간 쌓인 “broken-as-usual” 동작까지 사양의 일부가 돼 있다. 이 논문은 그 검증을 자동화하는 Locksmith Loop 를 제안한다.
  • 루프는 두 국면을 번갈아 돈다. Witness Search 는 여섯 개 탐색 알고리즘 (pairwise · 3-way · LHS · ART · MAP-Elites · UCB1) 으로 입력·목(mock) 공간을 훑어 도달 가능한 분기를 다 연다. 더 열리지 않는 지점 (plateau) 에 닿으면 Mutation 이 나서 Locked Paragraph 하나를 강제로 연다. 열린 순간 다시 Witness Search 로 돌아와 새 영역 안에서 재귀적으로 훑는다.
  • 결정적인 설계는 mutation 을 COBOL 목과 생성된 Java 양쪽에 대칭으로 적용한다는 것이다. 그래서 커버리지를 늘리는 코드 변형이 동시에 parity 검사의 대상이 된다. 판정은 LLM 이 아니라 결정론적 Parity Gate 가 paragraphs_hit · stub_log · terminal_state 세 축으로 내린다.
  • LLM (Authoring Layer) 은 마이그레이션 코드를 직접 쓰지 않는다. 막힌 지점에서 Mutation Skill 을 제안할 뿐이고, 채택 여부는 커버리지 증가와 Parity Gate 통과라는 두 결정론적 조건이 정한다. 생성과 판정의 분리가 이 논문의 뼈대다.
  • 세 프로그램 결과. CBACT01C (430 lines) 는 paragraph 16/16, transition 28/28, branch 60/62 (96.8%). CBSTM03A (924 lines) 는 paragraph 24/25 (96.0%), transition 37/38 (97.4%), branch 74.0%, parity 38/38 PASS. 사내 production-like 프로그램 (4,114 lines) 은 paragraph 135/142 (95.1%), transition 101/146 (69.2%), branch 91.90% (397/432), 166/166 PASS · 0 FAIL · 0 ERROR. 커버리지를 국면별로 분해해 보고한 뒤의 두 프로그램에서는 Mutation 이 연 전이가 Witness Search 가 연 것과 맞먹거나 더 많다.

소개 (Introduction)

메인프레임 위에서 도는 COBOL 은 아직도 은행·카드·보험의 코어에 남아 있다. 수십 년치 업무 규칙이 코드에 눌러 담겨 있고, 그중 상당수는 문서로 존재하지 않는다. LLM 기반 코딩 에이전트가 등장하면서 이 코드를 읽고 Java 로 옮기는 일 자체는 급격히 쉬워졌다. 문제는 그다음이다. 옮긴 결과가 원본과 같은 동작을 하는지를 어떻게 증명할 것인가.

현장의 실무는 대개 병행 운용이다. 레거시와 신규를 동시에 돌리며 출력이 갈리는지 지켜보다가, 충분한 확신이 쌓이면 트래픽을 넘긴다. 이 방식의 한계는 명확하다. 실제 트래픽이 밟는 경로는 전체 경로의 일부일 뿐이고, 나머지 긴 꼬리는 컷오버 이후에야 처음 실행된다. 게다가 레거시 시스템에는 원래 제품 정의와 어긋나지만 오랜 시간 동안 굳어져 사실상 사양이 된 동작 — 저자들이 “broken-as-usual” 이라 부르는 것 — 이 존재한다. lift-and-shift 형 현대화에서는 이 버그까지 정확히 재현해야 한다. 즉 검증의 목표는 정확성이 아니라 호환성이다.

American Express 팀이 내놓은 답은 이렇게 요약된다. 커버리지를 못 올리는 이유가 입력을 잘 못 고르기 때문이 아니라 하네스가 표현할 수 있는 입력 공간 바깥에 답이 있기 때문이라면, 입력을 더 뒤지지 말고 하네스의 경계를 넓혀라. 그리고 경계를 넓히는 변형을 레거시와 신규 양쪽에 똑같이 가하면, 그 변형 자체가 parity 검사의 대상이 된다. 이 두 문장이 Locksmith Loop 의 전부이고, 나머지는 그것을 감사 가능하고 재현 가능하게 만들기 위한 공학이다. 에이전트가 코드를 쓰는 시대에 “에이전트의 출력을 무엇으로 판정할 것인가” 라는 질문의 구체적인 한 가지 답이라는 점에서, COBOL 을 만질 일이 없는 사람에게도 읽을 값어치가 있다.

핵심 기여 (Key Contributions)

  • Locksmith Loop — 재귀적 하네스 확장을 검증 메커니즘의 중심에 놓은 방법론. 입력 공간 탐색 (Witness Search) 과 parity 보존 코드 변형 (Mutation) 을 번갈아 돌리되, 변형이 성공하면 새로 열린 영역 안에서 탐색을 재개한다. 저자들이 아는 한, 레거시-현대 마이그레이션 검증에서 재귀적 하네스 확장을 핵심 장치로 쓴 선행 연구는 없다.
  • Parity Gate — 에이전트 출력에 대한 결정론적 오라클. paragraphs_hit (진입한 paragraph 집합), stub_log (소비한 외부 연산의 순서 있는 시퀀스), terminal_state (실행 후 관측 가능한 변수 값) 세 축으로 COBOL 목과 생성 Java 의 behavioral fingerprint 를 비교한다. 마지막 라운드에 한 번 도는 검사가 아니라 모든 mutation 직후에 도는 상시 게이트다.
  • teacher-student 분리. student 는 마이그레이션·탐색·변형·parity 검사를 수행하는 결정론적 도구 집합이고, teacher 는 막힌 지점에서만 호출되는 감독 AI 에이전트 (Authoring Layer) 다. Authoring Layer 는 이관 코드를 직접 쓰지 않고 Mutation Skill 을 고르거나 새로 만들 뿐이다. TestGen-LLM 계열의 “LLM 이 제안하고 결정론적 필터가 판정한다” 패턴을 마이그레이션 검증에 이식한 형태다.
  • Mutation Skill Catalog — 성공한 변형의 재사용. 성공한 변형을 AI 스킬로 포착해 카탈로그에 넣고 이후 라운드에서 재사용한다. 코드를 직접 고치지 않고 스킬 형태로 유지함으로써 원본 프로그램 로직과 커버리지 확장용 변경을 분리한다.
  • 4,114 라인 production-shape 프로그램에서의 무인 실행 보고. 고객 데이터 없이 하네스 생성 입력과 목만으로 branch 91.90%, 166/166 parity PASS 를 사람 개입 없이 달성했다는 보고. 사내 프로그램이라 재현은 불가능하지만, 이 규모에서 무인 실행 결과를 수치와 함께 공개한 사례 자체가 드물다.

관련 연구 / 배경 지식

이 논문을 읽기 위한 최소 배경

COBOL 프로그램은 DIVISION → SECTION → paragraph 계층으로 구성된다. paragraph 는 레이블이 붙은 문장 묶음이고 사실상 함수에 해당한다. 제어 흐름은 PERFORM (호출), GO TO (점프), EVALUATE (switch), 그리고 악명 높은 ALTER (GO TO 대상의 런타임 변경) 로 흐른다. 이 논문의 Migrator 는 SECTION 하나를 Java 클래스 하나로, paragraph 하나를 메서드 하나로 사상한다.

커버리지 지표가 세 종류 나오는데 구분해서 읽어야 한다. paragraph coverage (P) 는 AST 상의 전체 paragraph 중 한 번이라도 진입한 비율, transition (edge) coverage (T) 는 정적으로 존재하는 paragraph→paragraph 제어 흐름 전이 중 한 번이라도 밟은 비율, branch coverage 는 계측된 분기 결과 (branch probe) 중 한 번이라도 통과한 비율이다. 뒤로 갈수록 엄격하고, 뒤에서 보겠지만 세 수치가 같은 프로그램에서도 꽤 크게 벌어진다.

네 갈래 선행 연구

논문은 자신의 위치를 네 영역의 교집합으로 규정한다.

1. 테스트 스위트 증폭과 하네스 testability. DSpot 처럼 기존 개발자 작성 테스트를 반복적으로 증강해 merge 가능한 패치로 되먹이는 계열, EvoSuite / EvoSuiteAmp 처럼 기존 테스트를 시드로 삼아 탐색 기반으로 개선하는 계열이 있다. Locksmith 도 반복 개선이라는 자세는 같지만, 변환 대상이 다르다. 테스트를 고치는 게 아니라 하네스 인터페이스 자체를 확장해서, 하네스가 표현할 수 있는 입력의 종류를 늘리고 그 넓어진 공간에서 Witness Search 를 다시 돌린다. testability transformation 계보에 인접하되, 프로그램을 테스트하기 쉽게 다시 쓰는 대신 controllability 경계를 반복적으로 밀어낸다.

2. 탐색 기반 입력 생성, 퍼징, 심볼릭 실행. combinatorial interaction testing, adaptive random testing, MIO, quality-diversity 계열의 MAP-Elites, 그리고 그 bandit 확장인 Monte Carlo Elites 가 Witness Search 의 재료다. OSS-Fuzz 실증 연구가 보여주듯 지속 퍼징의 진행은 느린 성장과 급격한 폭발이 번갈아 나오는 비선형 패턴을 띤다. Locksmith 는 초기엔 이 패턴을 그대로 따르다가, 여러 알고리즘이 같은 분기 수의 ±2–3 안으로 수렴하는 순간을 plateau 로 식별하고 방향을 튼다. 예산을 더 붓는 대신 하네스 경계를 변형해 다른 각도에서 탐색을 잇는다. 코드 변형으로 hard input check 를 뚫는다는 점에서 T-Fuzz 와 발상이 닮았지만, T-Fuzz 는 버그 발견이 목적이고 Locksmith 는 parity 게이트가 걸린 마이그레이션 검증이 목적이다.

3. differential / metamorphic 오라클. Mokav 나 DiffSpec 같은 LLM 보조 differential testing 은 같은 사양의 두 구현을 놓고 출력 차이를 버그로 신고한다. 정확한 출력을 알 수 없을 때는 metamorphic testing 이 입력과 출력 사이의 관계를 검사한다. Locksmith 의 Parity Gate 도 후보 입력을 판정하는 differential 오라클이지만, 동시에 변형을 위한 가드레일로도 쓰인다는 점이 다르다. 하네스 변형이 parity 를 유지하면서 도달 가능한 입력 공간을 넓히는지를 같은 게이트로 검사한다.

4. 프로그램 수리와 에이전틱 LLM 소프트웨어 공학. RepairAgent 는 LLM 도구 사용을 조율하고 프로그램 피드백으로 수정을 검증한다. Meta 의 TestGen-LLM 은 LLM 이 만든 테스트를 결정론적 필터가 빌드·통과·커버리지 향상을 확인한 경우에만 배포한다. TestPilot 은 실패 시 재프롬프트하고, Mut4All 은 버그 리포트로부터 컴파일러 퍼징용 mutator 를 합성한다. Locksmith 는 이 구조를 harness mutation 에 이식했다 — LLM 이 편집을 제안하고, 결정론적 analyzer / runner / parity 오라클이 어떤 편집이 살아남을지 정한다.

메인프레임 현대화 쪽 이웃

XMainframe 은 메인프레임 도메인에 특화한 LLM 이고, COBOL-coder 는 COBOL 코드 생성·번역에 도메인 적응한 모델이다. 그 밖에 high-resource Java refinement 를 지렛대 삼은 저자원 COBOL 번역, 프로그램 분석으로 LLM 을 보강한 엔터프라이즈 규모 COBOL-to-Java 파이프라인 연구가 있다. 가장 가까운 비교 대상은 Hans et al. 및 Kumar et al. 의 자동 검증 작업이다. 심볼릭 실행으로 COBOL 단위 테스트를 생성하고, 목을 곁들여 JUnit 으로 옮긴 뒤 의미 동치를 검사한다. 같은 문제를 겨냥하지만 배치가 다르다. 그쪽은 사후 검사 (post-hoc check) 이고, Locksmith 는 탐색-변형 루프 안에 게이트를 심는다.

범위와 전제

논문이 스스로 그은 경계도 분명하다. 대상은 COBOL → Java 의 1:1 이관이고, 성공 기준은 시스템 개편이 아니라 같은 입력 아래 parity 를 검증하면서 branch coverage 를 올리는 것이다. 하네스는 레거시 의존성을 의견 있는 방식으로 치환한다 — 레거시 RDBMS 는 PostgreSQL 로, MQ 는 RabbitMQ 로. 전략은 두 단계다. 먼저 parity 를 유지한 채 이관하고, 새 스택에서 확신이 선 뒤에 리팩터링한다.

방법 / 아키텍처 상세

Locksmith 은유

논문 전체가 자물쇠공 비유 위에서 설명된다. 분기 하나하나가 이고, 그 분기를 밟기 위해 필요한 입력과 목 응답이 열쇠다. 어떤 문은 쉽게 열리지만 어떤 문은 드릴이 필요하다 — 즉 막힌 실행 경로를 노출시키는 parity 보존 변형이 있어야 한다. 여러 탐색 알고리즘을 도달 가능한 분기 전체에 돌려 보면, 어느 문이 쉽게 열리고 어느 문이 반복 시도 후에도 잠긴 채인지 판별할 수 있다. 남은 분기는 현재 하네스 상태에서 구조적으로 도달 불가능한 것일 수 있다. 이때 드릴로 새 영역을 뚫고, 문이 열리면 다시 자물쇠 따기 (Witness Search) 로 돌아가 새로 도달 가능해진 경로를 훑는다. 이 과정을 재귀적으로 적용한다.

용어 다섯 개만 정리하면 나머지는 따라온다.

  • Locksmith Loop: 방법론 전체
  • Witness Search: 목 백엔드 응답과 환경 상태를 포함한 프로그램 입력 공간을 여러 알고리즘으로 탐색하는 국면
  • Locked Paragraph: Witness Search 만으로는 도달할 수 없는 COBOL paragraph
  • Mutation: parity 를 보존하는 코드 변형을 COBOL 과 Java 양쪽에 가하는 국면
  • Parity Gate: 같은 witness 입력으로 두 타깃을 돌려 종료 상태 불일치를 보고하는 결정론적 오라클

마이그레이션 하네스 구성

Figure 1: 마이그레이션 및 하네스 구성. 연파랑은 student 컴포넌트, 연노랑은 Authoring Layer 컴포넌트다. COBOL source 한 갈래는 Mock Generator 를 거쳐 실행 가능한 COBOL mock 이 되고, 다른 갈래는 Migrator 를 거쳐 Java target 이 된다. Runner / Parity Gate 가 둘을 같은 입력으로 몰아 동작을 비교한다.

COBOL 소스는 두 갈래로 흐른다. 한쪽은 사내 결정론적 Migrator 를 통과해 Java 타깃이 되고, 다른 한쪽은 결정론적 COBOL Mock Generator 를 통과해 실행 가능한 COBOL 목이 된다. 두 산출물은 같은 테스트 하네스가 몰고 다니며, 하네스는 behavioral fingerprint 를 기록하고 Parity Gate 를 집행한다.

전체 구조는 teacher-student 패턴이다. student 는 마이그레이션 · Witness Search · Mutation · parity 검사 파이프라인을 수행하는 결정론적 도구 집합이고, teacher 는 Authoring Layer 다. 여기가 핵심인데, Authoring Layer 는 이관 프로그램을 직접 쓰지 않는다. 막힌 실행 경로를 열기 위해 기존 스킬을 고르거나 새 스킬을 제안할 뿐이고, 제안의 옳고 그름은 Parity Gate 가 정한다. 컴파일 검사와 동작 동치 검사가 각 제안을 수락하거나 기각한다. parity 실패가 나면 Authoring Layer 가 개입하고, 그 변형이 게이트를 통과하면 루프가 재개된다. 생성과 결정론적 판정의 이 분리가 루프의 견고함을 떠받친다.

파이프라인 개요

파이프라인은 소스 수준 변환에서 시작한다. 파일 I/O, CALL, EXEC SQL, EXEC CICS 같은 외부 연산을 목으로 대체해 실행 가능한 mock COBOL 프로그램을 만든다. 목의 뒷단은 COBOL index file 이거나 PostgreSQL 같은 관계형 DB 다. 컴파일된 목 바이너리가 루프 내내 쓰이는 branch coverage 측정치를 공급한다.

그다음 외부 파서가 레거시 프로그램의 AST 를 만들고, 이것이 결정론적 Migrator 의 입력이 된다. Java 코드 생성기는 SECTION 당 클래스 하나, paragraph 당 메서드 하나를 뽑고, COBOL 제어 흐름 (PERFORM, GO TO, ALTER, EVALUATE) 을 등가의 Java 제어 흐름으로 사상한다.

테스트 케이스 하나는 input-statestub-state 의 쌍이다. input-state 는 실행 중 적용되는 변수 할당이고, stub-state 는 목 처리된 외부 연산이 돌려줄 레코드다. 실행이 끝나면 시스템은 behavioral fingerprint 를 기록한다 — 진입한 paragraph, 밟은 분기, stub 응답 로그, 종료 시점의 관측 가능한 상태. 이 fingerprint 가 커버리지 추적과 parity 비교의 단위가 된다.

네 컴포넌트

  • Deterministic Migrator: COBOL 소스를 Java 타깃으로 번역한다.
  • Deterministic Analyzer: AST 와 살아 있는 목을 살펴 프로그램 구조로부터 게이트를 식별한다.
  • Deterministic Runner: 바이너리를 컴파일하고, 테스트 케이스를 실행하고, branch coverage 를 측정하고, 세 동치 축을 따라 Parity Gate 를 적용한다. 코드 변형은 branch coverage 를 늘릴 때만 유지된다.
  • Agentic Authoring: 결정론적 analyzer 가 막힌 실행 경로에 닿았을 때 호출돼 새 스킬을 합성한다. Parity Gate 가 divergence 를 보고하면 Migrator 의 Java 코드 생성기 자체를 고치기도 한다.

Witness Search 스윕은 현재 반복의 COBOL 목 바이너리에 여섯 개 독립 알고리즘을 돌린다. 모두 같은 baseline 커버리지에서 출발한다. 하나의 시나리오는 입력 레코드, 초기 working-storage 값, 목 처리된 외부 CALL 의 file-status 값과 리턴 코드로 이뤄진다. 하네스 카탈로그가 시나리오별 허용 선택지를 추적하고, 여섯 알고리즘은 이 조합 공간을 어떻게 훑느냐만 다르다.

  • Pairwise interaction testing. 각 필드가 $|D_i|$ 개의 값 선택지를 갖는 $n$ 개 시나리오 컴포넌트가 있을 때, 가능한 모든 값 쌍 $(v_i \in D_i,\ v_j \in D_j)$, $i < j$ 를 덮는 작은 테스트 집합을 만든다. 최적 크기는 $O(|D|^2 \log n)$. COBOL 분기 상당수가 상관된 두 필드에 걸린 복합 IFEVALUATE WHEN 으로 게이팅돼 있어서 (status code × record type, account kind × balance sign) pairwise-covering 집합만으로도 두-필드 조합 하나하나가 최소 한 번씩 노출된다.
  • Three-way interaction. pairwise 를 삼중항으로 확장한다. status × record type × end-of-file flag 로 게이팅된 tail-handling paragraph 처럼, 세 값이 동시에 맞아야 열리는 소수의 분기를 잡는다.
  • Latin hypercube sampling (LHS). 레코드 수, 파일 길이, 금액처럼 준연속적인 컴포넌트에 쓴다. 각 도메인을 $N$ 개 구간으로 나눠 구간당 한 값을 뽑되, 컴포넌트 간 구간을 정렬해 거리 기반 다양성을 최대화한다. 특정 영역에 몰리지 않고 전 범위에 걸쳐 표본을 고르게 펼친다.
  • Adaptive random testing (ART). 이미 실행한 입력들과의 최소 거리를 최대화하는 입력을 다음으로 고른다. 프로그램 구조에 대한 사전 지식이 없을 때 특히 쓸모 있다.
  • MAP-Elites. quality-diversity 계열. behavioral descriptor 셀 격자를 유지하며 각 셀에 그 셀로 사상되는 최고 fitness 입력을 담는다. fitness 는 새로 덮은 분기 수다. COBOL 커버리지 프로파일은 소수의 실행 모양으로 뭉치는 경향이 있는데 (깔끔한 end-of-file 경로, 중간 절단, status-error 조기 종료, multi-record-type 배치), MAP-Elites 는 모양마다 좋은 테스트 케이스를 하나씩 남겨 희귀 분기 노출을 돕는다.
  • UCB1 bandit. 각 값 선택지를 bandit arm 으로 본다. 매 스텝에서 다음을 최대화하는 arm 을 고른다.
\[\begin{aligned} a^{\star} = \arg\max_{a} \left( \bar{x}_a + c\sqrt{\frac{\ln t}{n_a}} \right) \end{aligned}\]

여기서 $\bar{x}_a$ 는 새로 발견한 분기로 정의되는 경험적 평균 보상, $n_a$ 는 arm $a$ 가 선택된 횟수, $c$ 는 탐색 상수다. 위 조합 기반 방법들과 달리 UCB1 은 조합 폭발이 없다. 대신 한 번의 실행 안에서 어떤 값이 새 분기를 잘 여는지를 학습한다. COBOL 목에서는 status code 나 record type 처럼 많은 분기를 게이팅하는 정보량 큰 컴포넌트를 선호하게 된다.

한 번의 Witness Search 스윕 출력은 여섯 알고리즘이 발견한 분기의 합집합과 테스트 케이스 집합이다. 다음 스윕은 가장 좋은 집합에서 이어 가고, 두 번 연속 스윕이 새 분기를 하나도 못 내면 그 지점을 Witness Search plateau 라 부른다. mutation 이 하네스를 확장하고 나면 여섯 알고리즘 스윕을 처음부터 다시 돌린다 — 새로 도달 가능해진 영역은 다른 탐색 편향을 선호할 수 있기 때문이다. 저자들은 이 여섯이 실증적 실험으로 고른 조합이며 모든 마이그레이션에 최적이라는 보장은 없고, 고정된 처방이 아니라 대표적 집합으로 봐 달라고 명시한다.

Mutation: 스킬을 통한 parity 보존 하네스 변형

고전적 mutation testing 은 오류 처리를 검증하려고 애플리케이션에 결함을 주입한다. Locksmith 의 mutation 은 목적이 정반대다. 새 실행 경로를 발견해 강제로 열어 COBOL 과 Java 사이의 parity 를 확인하는 것이고, 그래서 두 코드베이스를 나란히 수정한다. 목표는 출력뿐 아니라 코드 트레이스까지 일치시키는 것이다.

결정론적 analyzer 가 후보 게이트를 짚고, Authoring Layer 가 그 게이트를 만족시키는 Mutation Skill 을 생성한다. 스킬은 새 dispatcher 경로를 추가하거나, 프로그램이 원래 외부에서 읽었을 값을 side channel (하네스 수준 stub hook) 로 노출시키거나, 도달하기 어려운 paragraph 의 실행을 강제한다. 스킬을 라운드 간에 지속시키고 직접 코드 수정을 피하는 이유는 원본 프로그램 로직과 커버리지 확장용 변경 사이의 경계를 유지하기 위해서다.

Mutation 국면이 끝나면 이전에 수락된 테스트 케이스를 다시 실행하고, 변형은 이후 반복을 위해 남긴다. paragraph 하나가 여러 독립 조건으로 막혀 있을 수 있으므로, 프레임워크는 식별된 게이트를 하나씩 평가해 추가 커버리지를 내는 변형만 남긴다.

스킬은 계층적 의존성을 가질 수 있고 필요한 순서대로 적용된다. 실무에서는 두 종류면 대부분 충분했다고 한다.

  • dispatcher-arm 스킬: 원래 외부에서 읽는 값을 side channel 로 직접 공급할 수 있게 하네스를 확장한다. 실행이 통제하기 어려운 값에 좌우되는 status-field 주도 로직에 특히 효과적이다.
  • call-injection 스킬: 메인 프로그램 흐름의 알려진 지점에서 타깃 paragraph 실행을 강제한다. cleanup 처리, end-of-file 처리처럼 흔치 않은 경로에서만 도는 로직을 실제로 돌려 볼 때 쓴다.

스킬 종류와 무관하게 같은 변경이 COBOL 구현과 Java 구현 양쪽에 적용되고, parity 는 밟은 실행 경로 · 수행한 외부 연산 · 최종 관측 상태를 비교해 검증한다.

논문이 실험 절에서 구체적으로 정의하는 변형이 force-set mutation 이다. 선택한 paragraph 진입 시점에, 외부 연산 (파일 읽기, DB fetch, 메시지 큐 수신, 하위 프로그램 호출) 이 목에서 돌려줬을 값을 선택한 값으로 덮어쓰되, COBOL 목과 그 생성 Java 타깃에 대칭으로 적용하는 parity 보존 변형이다.

Analyzer 가 게이트를 고르는 방식

Analyzer 는 정적 AST 리더다. 타깃 paragraph 마다 그 실행을 막는 제어 흐름 조건을 살펴 만족시켜야 할 게이트를 식별한다. 게이트가 특정 외부 값에 의존하면 필요한 값을 결정해 side channel 에 공급하고, 실행 중 그 값이 고정되게 한다.

여기 설계 판단이 하나 들어 있다. Analyzer 는 기존 프로그램 흐름을 통해 동작하는 해법 (dispatcher 기반 변형) 을 항상 선호한다. 그래야 paragraph 가 자연스럽게 도달되기 때문이다. 적당한 dispatcher 기반 접근이 없을 때만 call-injection 스킬을 권한다. 이 우선순위 덕에 AST 에 표현된 모든 paragraph 가 최소한 하나의 평가 가능한 후보 변형을 갖는다. Analyzer 는 의도적으로 보수적이어서, 통제 가능한 변수와 알려진 값 집합으로 명확히 설명되는 변형만 권한다. 모든 권고가 이해 가능하고, 감사 가능하고, 적용 전에 검증하기 쉽게 유지하기 위해서다.

한 Locked Paragraph 안의 형제 게이트 처리도 명시돼 있다. 타깃 paragraph 가 단일 조건으로만 막혀 있는 건 아니다. 중첩된 IF 조건, 감싸는 PERFORM 경계, 특정 EVALUATE 분기처럼 여러 제어 흐름 결정이 동시에 만족돼야 하는 경우가 많다. Analyzer 는 그런 게이트를 전부 식별하고 각각에 대한 후보 변형을 생성한다. 첫 성공에서 멈추지 않고 식별된 모든 게이트를 평가하는데, 각 게이트가 추가 실행 경로를 열 가능성이 있기 때문이다. 새 커버리지를 내는 변형은 active baseline 에 편입되고 이후 변형은 그 확장된 상태에서 이어진다. 추가 커버리지가 없으면 되돌린다. 최소 하나의 변형이 커버리지를 개선하면 그 paragraph 는 성공적으로 열린 것으로 본다.

재귀와 종료

Figure 2: Locksmith Loop 전체 제어 흐름. Witness Search 가 두 라운드 연속 새 분기를 못 내면 plateau 로 보고, Locked Paragraph 를 uncovered-branch 수로 정렬한 뒤 하나를 code mutation 으로 연다. Post-Mutation Witness Search (UCB1) 로 커버리지 이득을 확인하고 Parity Gate 가 COBOL ≡ Java 를 판정한다. 성공한 변형은 새로 열린 영역에서의 전체 스윕으로 제어를 되돌리고 (recurse: search from inside), 이득 없는 변형은 되돌려 실패로 표시한다.

루프는 (1) Witness Search 스윕과 (2) Mutation 스텝을 번갈아 돈다. 변형이 유지되려면 두 조건을 모두 만족해야 한다 — 커버리지를 늘려야 하고, Parity Gate 를 통과해야 한다. 둘 다면 하네스 변형을 유지하고 다음 Witness Search 스윕으로 넘어가는데, 이 스윕이 또 다른 Locked Paragraph 를 노출시킬 수 있다. 새로 나온 Locked Paragraph 도 같은 방식으로 정렬돼 변형된다. 둘 중 하나라도 실패하면 변형을 되돌리고 시도를 기록한다.

종료 보장은 단순하다. 이미 시도한 Locked Paragraph 는 다시 시도하지 않는다. 따라서 모든 재귀 스텝은 새 분기를 여는 parity 보존 변형을 얻거나, 아니면 Locked Paragraph 하나를 후보 목록에서 영구히 제거한다. 모든 Locked Paragraph 를 시도하고 나면 마지막 Witness Search 스윕으로 남은 분기를 회수한다.

Locked Paragraph 정렬 정책의 기본값은 bang × feasibility 다. uncovered branch 수와, 조건 변수 중 이미 하네스로 라우팅 가능한 비율의 합으로 점수를 매겨 “값어치 있으면서 실행 가능한” 것을 우대한다. 다만 실제 실험에서는 uncovered branch 수만 보는 greedy 정책을 썼다. 스킬 조회 (SKILLFOR) 는 카탈로그 룩업이지만 카탈로그가 불완전할 수 있어서, 대응 스킬이 없으면 Authoring Layer 가 게이트 분석으로부터 새 스킬을 만들거나, 타깃 paragraph · uncovered branch · 분기 조건 · active call stack 을 담은 구조화된 needs_new_skill 레코드를 생성한다.

최적화 목표와 수용 기준

학습 손실 대신 이 논문의 목적 함수 역할을 하는 건 수용 조건 두 개다. 변형 $m$ 이 유지되는 조건은

\[\begin{aligned} \text{keep}(m) \iff \; & \text{coverage}(\text{after } m) > \text{coverage}(\text{before } m) \\ & \wedge \; \text{ParityGate}(\text{COBOL}_m, \text{Java}_m) = \text{PASS} \end{aligned}\]

이고, 둘 중 하나라도 깨지면 되돌린다. 커버리지 조건만 있으면 하네스를 아무렇게나 뜯어 숫자를 올릴 수 있고, parity 조건만 있으면 아무것도 안 하는 변형이 항상 통과한다. 두 조건이 서로를 묶는다.

Parity Gate 의 세 동치 축

COBOL 쪽이 수락한 모든 테스트 케이스 (input-state + stub-outcomes) 는 생성된 Java 타깃으로도 몰아지고, 두 behavioral fingerprint 를 비교한다. 게이트는 세 축으로 판정하며, 축은 스킬별로 parity contract 에 선언된다.

비교 방식 잡는 결함
paragraphs_hit 진입한 paragraph 의 집합 비교 누락된 paragraph 순회, 조기 종료
stub_log 소비한 외부 연산 시퀀스의 순서 있는 비교 (공백 정규화 후 값 매칭) 잘못된 dispatch 순서, 누락/중복 외부 호출
terminal_state 실행 후 관측 가능한 변수 값의 점별 비교 (런타임 내부 bookkeeping 제외) 잘못 세팅된 변수, 값 정규화 오류

이 게이트는 ground-truth 사양 대신 compatibility 관계를 받아들이는 differential 오라클이다. 그리고 모든 mutation 이 양쪽에 동시에 구현되기 때문에, parity 는 마지막 라운드가 아니라 모든 break 직후에 연속적으로 검사된다.

divergence 는 parity intervention 채널에 구조화된 레코드로 방출된다 — divergence 종류, 사람이 읽을 설명, 기계가 읽을 diff 레코드. 이 레코드가 선택적 repair 스텝의 입력 계약이 된다. 루프는 기본적으로 divergence 를 기록하고 계속 진행하며, 그렇게 설정한 경우에만 멈춘다. 저자들의 실험에서는 모든 mutation 계층에 걸쳐 수락된 모든 테스트 케이스에서 게이트가 유지됐다.

Authoring Layer 개입 지점도 같은 형태다. analyzer 나 runner 가 스스로 해결할 수 없는 결정 지점에 닿으면 정해진 개입 지점에 구조화된 레코드를 쓰고 계속 간다. 그 레코드는 사람 운영자나 AI 에이전트가 비동기로 소비할 수 있다 — 루프가 사람을 기다리며 멈추지 않는다는 뜻이다.

대상 프로그램과 실험 셋업

세 개의 COBOL 코드베이스에 Locksmith Loop 를 돌렸다.

프로그램 소스 라인 paragraph 정적 전이 (edge) branch probe 출처
CBACT01C 430 16 28 62 AWS CardDemo (오픈소스)
CBSTM03A 924 25 38 146 AWS CardDemo (오픈소스)
production-like 배치 4,114 142 146 432 (정적 집계) 사내

production-grade 프로그램은 고객 데이터 없이 하네스가 생성한 테스트 입력과 목 처리된 외부 응답만으로 평가했다.

진행 차트 (Figure 3, 5) 의 지표 정의는 다음과 같다. P 는 AST 전체 paragraph 대비 진입한 paragraph 수, T 는 전체 정적 paragraph edge 대비 관측된 edge 수다. 각 edge 는 색으로 분류되는데, EXIT / GOBACK edge 는 목적지 노드의 DAG 색을 따르고 (range-completion 규칙: EXIT terminator 는 소스 paragraph 가 돌면 covered 로 센다), 비-EXIT edge 는 그 전이를 처음 관측한 국면을 따른다 (파랑 = Witness Search, 빨강 = Mutation, 겹치면 빨강 우선). 차트의 parity X/N PASS 는 생성 Java 타깃이 COBOL 실행의 paragraph 진입 · 외부 효과 · 출력을 정확히 재현한 수락 테스트 케이스 수, 즉 Parity Gate 를 살아남은 TC 비율이다.

실험 결과

작은 오픈소스 프로그램 — CBACT01C

430 라인, 62 branch probe 짜리 CBACT01C 에서 루프는 paragraph 커버리지 100% (16/16), transition 커버리지 100% (28/28), branch 커버리지 96.8% (60/62) 를 달성했다. 중간 복잡도 프로그램에서는 거의 완전 포화가 가능하다는 확인이다.

중간 크기 오픈소스 프로그램 — CBSTM03A

Figure 3: CBSTM03A 에 대한 커버리지 진행. 38 개 정적 edge 중 Witness Search 가 18 개 (파랑), Mutation 이 19 개 (빨강) 를 추가해 37/38 (97.4%), 미커버 1 개. paragraph 는 24/25 (96.0%), parity 는 38/38 PASS.

924 라인 CBSTM03A 에서 paragraph 24/25 (96.0%), transition 37/38 (97.4%) 이 나왔다. 여기서 눈여겨볼 건 기여 비율이다. 37 개 커버된 전이 중 Witness Search 가 연 건 18 개뿐이고 나머지 19 개는 Mutation 이 열었다. paragraph 도 24 개 중 8 개가 Mutation 몫이다. 즉 입력 공간 탐색만으로는 전이 기준 절반에 못 미치는 지점에서 멈춘다.

branch 커버리지는 다른 얘기다. 146 개 branch probe 중 38 개가 끝내 미커버로 남아 74.0% 에 그쳤다. 저자들도 Figure 3/4 가 paragraph 와 transition 커버리지를 보고하는 것이지 branch 커버리지가 아니라고 명시한다. 그러면서 “충분한 인내와 상당한 사람 개입이 있었다면 더 나은 결과를 얻었을 것이고, 현재 커버리지에서도 우리가 아는 어떤 대안 방법의 출력보다 낫다” 고 덧붙인다. 100% 를 막는 구조적 이유는 목킹의 복잡도 외에는 파악되지 않았다고 한다. 실패 사례도 구체적으로 밝히는데, Authoring Layer 가 한 스킬에서 배운 것을 관련 시나리오의 게이트를 여는 데 적용하지 못해 사람이 원인을 짚어 줘야 했다. 모델이 좋아지면 자율 실행 커버리지가 점진적으로 좋아지리라는 게 저자들의 기대다.

수락된 38 개 테스트 케이스는 전부 Parity Gate 세 축을 통과했다.

Figure 4: CBSTM03A 의 control-flow graph. 노드는 paragraph, 엣지는 정적 paragraph-to-paragraph 전이다. 노드 색은 그 paragraph 에 처음 도달한 국면을 뜻한다. 고립된 파랑/회색 노드가 보이는 건 정상인데, 순차적 COBOL fall-through 를 명시적 엣지로 모델링하지 않았기 때문이다. 엣지 색은 파랑 = Witness Search 관측, 빨강 = mutation 이후 관측 (겹치면 빨강 우선), 회색 = 이번 실행에서 미도달.

Figure 4 는 이 구조를 그대로 보여 준다. 저자들의 설명대로 파랑 영역이 Witness Search (입력 공간 탐색) 가 연 첫 단계이고, 빨강이 Mutation 의 결과, 회색이 미커버 영역이다. 그래프 아래쪽 한 노드로 빨간 엣지가 다발로 수렴하는 모습이 눈에 띄는데, mutation 이후에야 관측된 전이가 특정 paragraph 로 몰려 있다는 뜻이다.

production-shape 실행

Figure 5: production-shape 실행의 커버리지 진행. 146 개 정적 paragraph 연결 중 baseline Witness Search 가 54 개 (파랑), Mutation 이 47 개 (빨강) 를 추가해 T=101/146 (69.2%). paragraph 커버리지는 166 개 테스트 케이스에 걸쳐 P=135/142 (95.1%).

두 번째 실험은 4,114 소스 라인, 142 paragraph, 432 정적 분기의 훨씬 큰 production-grade 프로그램이다. 최종 수치는 paragraph 135/142 (95.1%), transition 101/146 (69.2%), branch 91.90% (397/432).

진행 과정이 특히 선명하다. Witness Search 국면은 P=110/142, T=54/146 에서 멈췄고, Mutation 국면이 ΔP=+25, ΔT=+47 을 더해 P=135/142, T=101/146 에서 plateau 에 들어갔다. 실행 수로 보면 Witness Search 가 61 회, Mutation 이 105 회로 합계 166 회다. 즉 실행의 3분의 2가 Mutation 국면에서 나왔고, 그 대가로 전이 47 개를 추가로 열었다. 이미 plateau 에 닿은 지점에서 출발해 Witness Search 가 연 54 개에 육박하는 47 개를 더 열었다는 게 이 논문의 주장을 가장 직접적으로 뒷받침하는 숫자다.

각 TC 마다 parity 검사가 돌았고, 불일치가 검출되면 새 수정 세트를 Authoring Layer 가 제안해 Migrator 나 COBOL Mock Generator 에 적용했다. 저자들이 열거한 불일치 유형은 다음과 같다 — end-of-stream 및 종료 의미론, 제어 흐름 충실도, 프로그램 상태·데이터 레이아웃 충실도, 데이터 타이핑과 값 정규화, DB 및 목 백엔드 동작 동치, 파일 I/O 부수 효과와 순서 동치, fixture 및 환경 일관성, 측정·정규화 아티팩트 통제. 최종적으로 생성 Java 타깃은 모든 parity 축에서 COBOL 동작을 재현했다 — 166/166 PASS, 0 FAIL, 0 ERROR. 그리고 루프는 사람 개입 없이 진행됐다.

Figure 6: production 프로그램의 control-flow graph. Figure 4 와 같은 색 규약이다. clutter 를 줄이려고 COBOL fall-through 의미론을 생략했으므로 모든 실행 경로가 DAG 에 표현돼 있지는 않다.

결과 분석 / Ablation

여섯 알고리즘이 같은 벽에 부딪힌다

가장 흥미로운 관찰은 여섯 Witness Search 알고리즘이 거의 같은 커버리지 (±2–3 분기) 에서 끝난다는 것이다. 저자들은 성능이 나빠 폐기한 알고리즘까지 더 실험한 뒤, 이 경계가 알고리즘적인 것이 아니라 구조적이라고 일반화한다. 즉 남은 분기들은 하네스가 표현할 수 있는 입력 공간 바깥에 있고, 어떤 탐색 전략을 써도 도달할 수 없다.

이 관찰이 논문 전체 논리의 축이다. 탐색 예산을 늘리는 게 답이 아니라는 진단이 있어야 하네스 변형이라는 처방이 정당해진다. 다만 리뷰어 입장에서 같은 관찰은 다른 해석도 허용한다 — 여섯 알고리즘이 같은 결과를 낸다면 여섯 개를 다 돌릴 이유가 없다. 실제로 Post-Mutation Witness Search 는 UCB1 하나만 쓴다. 저자들이 이를 명시적으로 논하지는 않지만, 이 설계 자체가 앙상블의 중복성을 인정하는 셈이다.

Mutation 이 얼마나 기여했나

두 실행 모두에서 Mutation 의 기여가 Witness Search 에 필적하거나 앞선다.

실행 Witness Search Mutation 미커버
CBSTM03A (transition) 18/38 +19/38 1/38
CBSTM03A (paragraph) 16/25 +8/25 1/25
production (transition) 54/146 +47/146 45/146
production (paragraph) 110/142 +25/142 7/142

CBSTM03A 에서는 Mutation 이 열어 준 19 개 전이가 Witness Search 가 연 18 개보다 많다. production 실행에서도 47 대 54 로 비슷한 규모다. 저자들이 threats 절에서 정직하게 표현하듯, 이 수치는 “Mutation 국면이 입력 탐색만으로는 닿을 수 없는 paragraph 와 전이에 닿는다” 는 사례 증거이지 모든 COBOL 시스템에 대한 일반 주장은 아니다.

깊이-2 재귀가 실제로 작동한다

논문이 명확히 보고하는 재귀의 실증이 하나 있다. depth-1 게이트를 열고 Witness Search 를 진행한 뒤, 에이전트가 두 번째 층의 게이트를 열자 여섯 알고리즘이 다시 발동해 이 층을 뚫을 새 witness 집합을 찾아냈다. Mutation 의 가치는 그것이 직접 여는 분기에만 있는 게 아니라 그것이 노출시키는 영역에 있다는 저자들의 표현이 이 지점을 정확히 짚는다.

루프의 각 패스에서 검출된 결함은 생성된 코드가 아니라 Migrator 에서 고쳐진다. Authoring Layer 가 Migrator 수정을 제안하는 방식이고, 그 결과 parity 를 달성한다. 이건 실무적으로 중요한 차이다. 생성물을 손보면 다음 재생성에서 사라지지만, 생성기를 손보면 같은 패턴의 모든 프로그램에 반영된다.

남은 커버리지 갭의 패턴

production 실행이 100% 에 못 미친 이유를 저자들은 구조적 · 외부 결합 복잡도로 돌린다. 미커버 paragraph 를 뜯어보니 일관된 패턴이 있었다. 남은 경로들은 여러 단계에 걸친 paragraph 체인 뒤에 앉아 있고, 제어 변수가 앞선 섹션들에서 점진적으로 설정된다. 이 paragraph 에 진입하려면 가짜 파일과 DB 가 정확히 조율된 결과 시퀀스를 돌려주고, 내부 스위치를 이른 시점에 뒤집어야 한다. 단일 게이트 하나를 여는 것으로는 안 되고, 시간축에 걸친 상태 시퀀스를 통째로 맞춰야 하는 종류의 잠금이다.

결론부에서 저자들은 남은 갭이 방법의 구조적 한계라기보다 현재 Authoring Layer 의 한계를 반영한다고 본다. 간헐적인 mutation 저작 오류, 그리고 학습한 전략을 관련 시나리오로 전이시키지 못하는 실패가 원인이었고 사람 개입으로 해소됐다. 결정론적 코어는 애초에 에이전트 층이 사람 도움을 받는 상황에서도 효과적으로 동작하도록 설계됐다는 것이 그들의 방어 논리다.

한계와 비판적 평가

저자가 인정한 것

  • 사례 수가 적다. 오픈소스 두 개, production-shape 한 개. IMS, DB 중심 SQL, CICS 시스템 등 다른 프로그램 계열로 복제해야 주장이 강해진다.
  • branch coverage 는 불완전한 대리 지표다. 커버리지 높은 스위트가 모든 결함 부류를 못 잡을 수 있고, 잘 고른 소수의 mutation 테스트가 더 나을 수 있다는 건 퍼징·커버리지 문헌의 오랜 긴장이다. 저자들은 커버리지가 아니라 교차 언어 parity 에 수락을 걸어 완화한다고 하면서도, 결과 스위트가 마이그레이션 승인에 그 자체로 충분하다고는 주장하지 않는다. “bare-baseline 시작점 대비 정량 비교 가능한 잣대일 뿐” 이라는 표현이 정확하다.
  • parity 는 버그도 보존한다. 레거시 COBOL 을 operational reference 로 삼는다는 건 이관된 Java 가 레거시의 버그까지 재현하는지를 검증한다는 뜻이다. 1:1 마이그레이션 맥락에서는 이게 원하는 성질이지만, 저자들의 표현대로 operational reference 는 compatibility 의 진실이지 semantics 의 진실이 아니다. 최종 목표가 호환성 이관이 아니라 정확성 검증이라면 Parity Gate 만으로는 부족하고 외부 사양 오라클이 필요하다.
  • 하네스 구조에 과적합될 수 있다. Locksmith 는 본 적 있는 소스 프로그램에서만 배우므로, 초기 표본에 없는 구조나 실행 패턴은 포착 못 할 수 있다.
  • Parity Gate 는 자기 축만큼만 강하다. 세 축은 paragraph 순회 누락, 조기 종료, 잘못 세팅된 변수, 잘못된 dispatch 순서를 잡는다. 하지만 관측 가능한 체크포인트 사이의 중간 상태, COBOL 과 Java 런타임의 부동소수점 반올림 모드 차이, JVM 전용 오류 경로가 던지는 예외 타입에 대해서는 침묵한다. 검사하지 않는 축에서 동작이 어긋나면서도 게이트를 통과하는 Java 타깃이 원리적으로 가능하다.

리뷰어 관점에서 추가로 보이는 것

베이스라인 비교가 없다. 논문은 가장 가까운 선행 작업으로 Hans et al. / Kumar et al. 의 심볼릭 실행 기반 COBOL-Java 검증을 지목하지만, 정작 그것과 같은 프로그램에서 head-to-head 를 돌리지 않는다. “우리가 아는 어떤 대안 방법의 출력보다 낫다” 는 문장은 측정이 아니라 주장이다. CBACT01C 와 CBSTM03A 는 공개 프로그램이므로 EvoSuite 계열이나 기존 COBOL 테스트 도구와의 비교가 불가능하지도 않았을 텐데, 그 비교가 빠진 자리가 크게 보인다.

커버리지를 측정하는 대상이 변형된 프로그램이다. 이게 방법론의 근본 긴장이다. 91.90% 라는 branch coverage 는 원본 프로그램이 아니라 하네스 변형이 가해진 바이너리의 커버리지다. call-injection 스킬이 메인 흐름의 알려진 지점에서 paragraph 실행을 강제하면, 그 paragraph 가 두 언어에서 동일하게 도는지는 증명되지만 실제 입력으로 그 paragraph 에 도달할 수 있는지, 그리고 도달하는 실제 제어 흐름이 동일한지는 증명되지 않는다. Analyzer 가 dispatcher 기반 변형을 우선한다는 설계가 이 문제를 완화하려는 장치인 건 분명하지만, 최종 커버리지 중 얼마가 dispatcher 기반이고 얼마가 call-injection 기반인지는 보고되지 않는다. 이 비율이야말로 결과의 해석을 좌우하는 숫자다.

abstract 의 커버리지 서술이 관대하다. “두 오픈소스 프로그램에서 거의 완전한 커버리지에 도달했다” 는 문장은 paragraph / transition 기준으로는 맞지만 (16/16, 28/28 그리고 24/25, 37/38), CBSTM03A 의 branch 커버리지는 74.0% 다. 같은 문장에서 production 프로그램에 대해서는 “91.90% branch coverage” 라고 branch 지표를 쓰기 때문에, 독자가 앞의 “거의 완전한” 도 branch 로 읽을 여지가 크다. 본문은 지표를 명확히 구분하지만 abstract 는 그렇지 않다.

확률적 방법인데 반복 실행이 없다. ART, MAP-Elites, UCB1 은 전부 난수를 쓴다. 프로그램당 실행이 한 번씩이고, 시드도 분산도 보고되지 않는다. “여섯 알고리즘이 ±2–3 분기 안에서 수렴한다” 는 관찰조차 반복 없이는 얼마나 견고한지 알 수 없다.

비용이 보고되지 않는다. “루프가 몇 시간 동안 무인으로 돌았다” 는 서술이 전부다. 토큰 비용, wall-clock, LLM 호출 수, 어느 모델을 썼는지 — 전부 없다. 저자들 자신이 future work 에서 토큰 비용 측정을 과제로 꼽는 걸 보면 의도적 누락이라기보다 아직 안 한 것에 가깝지만, 이 방법을 도입할지 판단해야 하는 실무자에게는 가장 먼저 필요한 숫자다.

정책·구성요소 ablation 이 없다. Locked Paragraph 정렬 정책은 기본값 (bang × feasibility) 을 설명해 놓고 실험은 greedy 로 돌렸는데, 둘의 비교가 없다. 두 스킬 타입 중 어느 쪽이 얼마나 기여했는지도 없다. 여섯 알고리즘 중 몇 개를 빼면 어떻게 되는지도 없다. 방법의 어느 부분이 실제로 값어치를 만드는지 분해되지 않은 채로 남는다.

paragraphs_hit 이 집합 비교다. 진입 순서는 이 축에서 검사되지 않는다. 순서 정보는 stub_log 를 통해 간접적으로만 잡히므로, 외부 연산을 소비하지 않는 paragraph 들 사이의 순서가 뒤바뀐 Java 타깃은 이론상 게이트를 통과할 수 있다. 저자들이 (e) 항에서 인정한 “검사하지 않는 축” 목록에 이 항목도 들어갈 만하다.

시사점 / Takeaways

  • 탐색이 막혔을 때 예산이 아니라 경계를 의심하라. 여섯 개의 서로 다른 탐색 알고리즘이 같은 지점에서 멈춘다면 그건 알고리즘 문제가 아니다. 하네스가 표현할 수 있는 입력 공간이 답을 담고 있지 않다는 신호다. 이 진단-처방 쌍은 COBOL 과 무관하게 어떤 테스트 자동화에도 옮길 수 있다.
  • LLM 을 생성자로 쓰고 판정은 결정론적 오라클에 맡기는 배치가 반복해서 이긴다. TestGen-LLM 이 그랬고 이 논문도 같다. 여기서 한 걸음 더 나간 건, 판정 오라클을 파이프라인 끝이 아니라 루프 안에 심어 매 변형마다 돌린다는 점이다. 잘못된 변형이 축적될 시간을 주지 않는다.
  • 레거시 이관 검증의 목표는 정확성이 아니라 호환성이다. 이 구분을 명시적으로 세우고 오라클을 그에 맞춰 설계한 게 이 논문의 가장 실무적인 기여다. 컷오버 이후 Java 코드를 유지보수할 사람들에게 레거시 동작 이탈은 조사해야 할 회귀이지 개선이 아니다. 다만 이 선택은 되돌리기 어렵다 — 정확성 검증으로 넘어가려면 완전히 다른 오라클이 필요하다.
  • 결함을 생성물이 아니라 생성기에서 고쳐라. parity 실패를 Java 산출물에 패치하는 대신 Migrator 에 반영하는 설계는 사소해 보이지만 규모가 커질수록 결정적이다. 같은 COBOL 관용구가 수백 개 프로그램에 반복되는 환경에서 생성기 수정 하나가 전부에 전파된다.
  • 커버리지 숫자를 볼 때 그 숫자가 어떤 프로그램에 대한 것인지 물어라. 하네스를 변형해서 얻은 커버리지는 원본 프로그램의 커버리지와 다르다. 이 논문의 방법은 그 차이를 parity 로 방어하지만, 도입을 검토한다면 강제로 연 경로의 비율을 반드시 따로 집계해야 한다.

참고 자료

더 읽어보기




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • [논문 리뷰] Accurate Decoding of Natural Sentences from Non-Invasive Brain Recordings
  • [논문 리뷰] RAGU: A Multi-Step GraphRAG Engine with a Compact Domain-Adapted LLM
  • [논문 리뷰] Hierarchical Self-Improvement: A Framework for Task-Specific Evolvable Agent Harnesses
  • [논문 리뷰] NVIDIA-labs OO Agents: Native Python Object-Oriented Agents