Skip to content

feat(core): KeyTable T를 오토마타 상태로, 가상 단위 A/B/C 서열 유지 - #7

Merged
lens0021 merged 2 commits into
mainfrom
feat/sinsebeol-automata-state
Jul 12, 2026
Merged

feat(core): KeyTable T를 오토마타 상태로, 가상 단위 A/B/C 서열 유지#7
lens0021 merged 2 commits into
mainfrom
feat/sinsebeol-automata-state

Conversation

@lens0021

Copy link
Copy Markdown
Collaborator

Closes #4

배경

teogeul 이행 1단계에서 상태 의존 자판(신세벌식 3종, 3-2015 계열)을 옮기지 못했다. 신세벌식은 반모음 진행을 물리적으로 다른 두 키로 구분한다(오른손 ㅗ=겹모음 진행 vs 왼손 ㅗ=종성 갈마들이 대기). 둘 다 놓이는 실제 낱자는 똑같이 ㅗ라서, 현재 KeyTable 값-식 변수로는 이 둘을 구분할 수 없었다.

변경

이슈에서 제안한 두 확장을 그대로 구현했다.

  1. press()T = 오토마타 상태: AutomataTable이 있는 항목에서는 t_state() 휴리스틱(0/1/2) 대신 auto_state를 그대로 낸다. 날개셋 도움말의 정의(T=현재 오토마타 상태)와 일치하며, 표준 설정(상태 id 의미가 휴리스틱과 같은 0/1/2)에서는 동작이 그대로다. AutomataTable이 없으면 지금처럼 휴리스틱.

  2. 오토마타 A/B/C에 가상 단위 식별 유지: feed_unitUnit::Virtual을 풀어서 오토마타에 넘기던 걸, incoming_role_seq()로 바꿔 가상 단위면 VIRTUAL_SEQ_BASE(1000) + id(실제 서열표 최댓값보다 훨씬 큰 값이라 절대 충돌하지 않음)를, 보통 단위면 풀린 자모의 날개셋 서열을 그대로 A/B/C에 낸다. 배치(자리 배정)는 지금처럼 풀린 자모로 한다. fresh_state/automaton_feed/apply_result가 이 서열(seq)을 함께 실어 나르도록 시그니처를 조정했다.

검증

이슈의 검증 계획(과/곶/갓)을 신세벌식형 합성 오토마타로 오라클 테스트화했다(가상 단위 200=오른손ㅗ/201=왼손ㅗ, T로 갈라지는 상태 의존 글쇠 f):

  • sinsebeol_right_hand_o_forms_compound_vowel: 과 = ㄱ+p(가상200→ㅗ)+f(T==3→중성ㅏ, UnitMix로 ㅘ)
  • sinsebeol_left_hand_o_becomes_toggle_jong: 곶 = ㄱ+v(가상201→ㅗ)+f(T==4→종성ㅈ)
  • sinsebeol_same_key_toggles_jong_after_jung: 갓 = ㄱ+f(중성ㅏ)+f(T==2&&E==1→종성ㅅ로 갈마듦)
  • sinsebeol_plain_syllable: 기본 경로 무회귀 확인용 베이스라인

두 수정 각각을 일부러 되돌려서 정확히 그 테스트들이 실패하는 것까지 로컬에서 확인했다(가짜 통과 방지):

  • T 수정만 되돌리면 sinsebeol_left_hand_o_becomes_toggle_jong(곶)만 실패 — 과는 우연히 else 분기가 같아 안 걸림.
  • 가상 단위 서열 수정만 되돌리면 과·곶 둘 다 실패(p/v가 똑같이 진짜 ㅗ 서열로 뭉개져 상태 3/4 전이 자체가 안 걸림).

기존 104개 + 신규 4개 전부 통과, cargo test --workspace(oracle/schema 포함) 무회귀, cargo fmt/clippy 클린.

🤖 Generated with Claude Code

lens0021 and others added 2 commits July 12, 2026 22:51
신세벌식 갈마들이(오른손 ㅗ/ㅜ=겹모음 진행 vs 왼손 ㅗ=종성 갈마들이 대기)를
지원하기 위한 두 가지 확장.

1. press()의 값-식 문맥에서 T: AutomataTable이 있으면 t_state() 휴리스틱
   (0/1/2) 대신 auto_state를 그대로 낸다. 날개셋 도움말의 정의(T=현재
   오토마타 상태)와 일치. 표준 설정(상태 id 의미가 휴리스틱과 같은 0/1/2)
   에서는 동작이 그대로다 - 기존 104개 테스트 전부 무회귀로 확인. 상태
   의존 자판만 3 이상의 상태를 정의해 "겹모음 진행 중"류 진행 상태를 T로
   구분할 수 있게 된다.

2. 오토마타 A/B/C에 가상 단위(VirtualUnitTable) 식별 유지: 이전에는
   feed_unit이 Unit::Virtual을 풀어서 오토마타에 넘겨, 가상 단위의 정체가
   사라지고 풀린 자모의 서열만 남았다. 신세벌식에서 물리적으로 다른 두
   키(오른손/왼손 ㅗ)가 같은 실제 낱자(ㅗ)를 내면서도 서로 다른 진행
   상태로 이어져야 하는데, 풀린 자모만으로는 구분이 불가능했다.
   incoming_role_seq()를 새로 두어, 가상 단위면 VIRTUAL_SEQ_BASE(1000)+id
   를(실제 서열표 최댓값보다 훨씬 큰 값이라 절대 충돌하지 않음), 보통
   단위면 풀린 자모의 서열을 그대로 A/B/C에 낸다. 배치(자리 배정)는
   지금처럼 풀린 자모로 한다 - fresh_state/automaton_feed/apply_result에
   seq를 함께 실어 나른다.

곁들여 feed_unit의 Unit→Jamo 해석을 이미 있던 resolve_jamo 헬퍼로
재사용(중복 제거 없음, 이미 PR #3에서 정리됨).

검증: 신세벌식형 합성 오토마타(가상 단위 200=오른손ㅗ/201=왼손ㅗ, T로
갈라지는 상태 의존 글쇠)로 과(겹모음)·곶(종성 갈마들이)·갓(중성 뒤
같은키 종성 갈마들이) 오라클 테스트 4개 추가. 두 수정 각각을 일부러
되돌려서 정확히 그 테스트들이 실패하는 것까지 확인(가짜 통과 아님):
T 수정 되돌리면 곶 실패, 가상 단위 서열 수정 되돌리면 과·곶 둘 다 실패.
기존 104개 + 신규 4개 전부 통과, oracle/schema 테스트 무회귀.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Opus 서브에이전트 리뷰(PR #7)가 찾은 확정 버그 수정.

1. automaton_feed/apply_result/feed_jamo_tracked 가 이력에 풀린 자모
   (Unit::Jamo)만 남기고 있었다. 정상 입력 때는 incoming_role_seq 가
   가상 단위 서열을 A/B/C 에 정확히 냈지만, 재생(replay_history, 즉
   백스페이스·BkspAttach 되살리기·도깨비불·성분 이동 등 이력 기반 편집
   전부)은 이력에서 다시 feed_unit 을 태우므로, 이력이 가상 단위 원형을
   잃으면 재생 때는 풀린 자모의 "진짜" 서열로 오토마타가 다시 태워져
   가상 단위로만 갈 수 있는 상태 전이를 못 찾고 낱자를 통째로 잃었다
   (곶 완성 후 백스페이스 한 번 → 고 여야 하는데 ㄱ까지 날아감). 세
   함수 모두 원래 Unit 을 이력 기록용으로 함께 받도록 시그니처를
   바꿔, 배치는 지금처럼 풀린 자모로 하되 이력은 원형을 남긴다(레거시
   경로가 이미 하던 방식과 통일).

2. commit_and_clear(공백·기호·미배열 글쇠 통과 등 조합 흐름을 끊는
   확정)가 auto_state 를 안 돌리고 있었다. flush()/reset() 은 이미
   시작 상태로 되돌리는데 이 경로만 빠져 있어서, 확정 직후 auto_state
   가 방금 끝난 음절의 상태로 남고 이제 이 값이(#4 이후) T 로 그대로
   노출되므로 다음 글쇠의 값-식이 방금 끝난 음절의 상태를 잘못 읽을 수
   있었다.

회귀 테스트 2개 추가(sinsebeol_backspace_unwinds_virtual_unit_one_step,
sinsebeol_commit_and_clear_resets_automata_state). 각 수정을 일부러
되돌려 정확히 그 테스트만 실패하는 것까지 확인. 곁들여 리뷰가 지적한
테스트 주석 부정확성도 정정(과는 T 수정 없이도 우연히 통과함, 실제로
T=auto_state 를 요구하는 건 곶 하나뿐).

기존 108개 + 신규 2개 전부 통과, oracle/schema 무회귀, fmt/clippy 클린.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@lens0021

Copy link
Copy Markdown
Collaborator Author

서브에이전트(Opus) 리뷰 결과 및 수정

Opus로 독립 리뷰를 받았고, 확정 버그 1개와 latent 이슈 1개를 찾아 반영했습니다(069ec18).

확정 버그: 가상 단위로 만든 음절에 백스페이스/이력 편집을 하면 낱자가 통째로 사라짐

automaton_feed/apply_result/feed_jamo_tracked가 이력에 풀린 자모(Unit::Jamo)만 남기고 있었습니다. 정방향 입력에선 incoming_role_seq가 가상 단위 서열을 A/B/C에 정확히 내지만, 재생(replay_history — 백스페이스, BkspAttach 되살리기, 도깨비불, 성분 이동 등 이력 기반 편집 전부)은 이력에서 다시 feed_unit을 태우므로, 이력이 가상 단위 원형을 잃으면 재생 때는 풀린 자모의 "진짜" 서열로 오토마타가 다시 태워져 가상 단위로만 갈 수 있는 상태 전이를 못 찾고 낱자를 통째로 잃습니다.

재현: 곶(rvf) 완성 후 백스페이스 한 번 → 기대값 "고"인데 실제로는 "ㄱ"까지 날아감(v로 놓은 ㅗ 자체가 증발).

수정: 세 함수 모두 원래 Unit을 이력 기록용으로 함께 받도록 시그니처를 바꿔, 배치는 지금처럼 풀린 자모로 하되 이력엔 원형(Unit::Virtual)을 남기도록 했습니다. 레거시 경로가 이미 하던 방식과 통일된 것뿐입니다.

추가로 잡은 latent 이슈: commit_and_clearauto_state를 안 돌림

flush()/reset()은 이미 auto_state를 시작 상태로 되돌리는데, 공백·기호·미배열 글쇠 통과에 쓰이는 commit_and_clear만 빠져 있었습니다. #4 이후 auto_stateT로 그대로 노출되므로, 확정 직후 다음 글쇠의 값-식이 방금 끝난 음절의 상태를 잘못 읽을 수 있는 latent 버그였습니다(리뷰가 지적, SINSEBEOL 픽스처만으로는 관측 가능한 오조합을 못 만들었지만 실제 신세벌식류 자판에선 걸릴 수 있다고 판단해 같이 고침).

검증

회귀 테스트 2개 추가(sinsebeol_backspace_unwinds_virtual_unit_one_step, sinsebeol_commit_and_clear_resets_automata_state). 각 수정을 일부러 되돌려 정확히 그 테스트만 실패하는 것까지 확인(가짜 통과 아님). 리뷰가 지적한 테스트 주석 부정확성도 정정(과는 T 수정 없이도 우연히 통과함 — 실제로 T=auto_state를 요구하는 건 곶 하나뿐).

기존 108개 + 신규 2개 전부 통과, cargo test --workspace(oracle/schema 포함) 무회귀, fmt/clippy 클린.

리뷰가 확인해준 부분(변경 없음): VIRTUAL_SEQ_BASE=1000 충돌 없음, seq 스레딩 리팩터링 완전성, 신세벌식 픽스처의 서열 숫자(ㄱ=1, ㅏ=1, ㅅ=109, ㅈ=161, ㅗ=21)와 UnitMix 규칙 전부 정확.

@lens0021
lens0021 merged commit 3d44dd4 into main Jul 12, 2026
1 check passed
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.

KeyTable T 를 오토마타 상태로, 오토마타 A 에 가상 단위 식별 유지 (신세벌식 갈마들이)

1 participant