Files
doslang-mirror/handoff2.md
T
coolguy cb20cce81a docs: TODO 와 핸드오프 문서를 추적한다
지난번 에이전트가 handoff1 만 지우라는 지시에 두 문서까지 같이 지웠다.
untracked 였기 때문에 git 에 기록이 없어 복구할 수 없었다.
2026-08-17 05:08:07 +09:00

9.4 KiB

Handoff 2 — 마커 증거 수집과 fixture 이름 짓기

앞선 시도가 한 번 실패해서 되돌렸다(7636a41). 실패 원인부터 읽어라. 그게 이 문서의 절반이다.

실패한 방식

지난번 에이전트는 파일을 열지 않고 이름 문자열만 변환했다. 밑줄을 지우고, 그 결과 충돌하는 이름에 숫자를 붙였다.

bad_loop  →  badlop1        badweak  →  badweak2
badloop   →  badlp2         oktry    →  oktry2

결정적 증거는 이것이다. own/badfld.fe구조체 필드에 참조를 둔 것을 검사하고 types/badfld.fe없는 필드에 접근한 것을 검사한다. 완전히 다른 규칙인데 둘 다 badfmem.fe 가 됐다. 같은 옛 이름에 같은 치환을 먹였기 때문이다.

이 일의 본질은 파일을 읽고 무엇을 검사하는지 판단하는 것이다. 이름 변환이 아니다.


배경

fec 는 Ferro 언어의 컴파일러이고 현재 프론트엔드만 있다. fixture 는 컴파일러가 어떤 코드를 받아들이고 어떤 코드를 거부하는지 고정하는 테스트다. 파일 하나가 케이스 하나다.

uv run python tests/run.py            # 전체
uv run python tests/run.py -k own     # 경로에 own 이 들어간 것만

러너는 매번 .build\fec.exe 를 새로 빌드한다. 진단 전문을 보려면 그걸 직접 부른다.

> .build\fec.exe --check fec\tests\types\bad_ari.fe
fec/tests/types/bad_ari.fe:8:15: error: wrong number of arguments
  8 |     return add(1);
    |               ^

러너가 기대를 정하는 방법 — 이걸 정확히 알아야 한다

tests/run.pyexpectation() 을 직접 읽어라. 요약하면:

파일 첫 줄 러너의 기대
// ERROR:7:borrow 거부되고, 진단이 7번 줄, 문구에 borrow 포함
// ERROR:borrow 거부되고 문구에 borrow 포함 (줄은 안 봄)
마커 없음 파일명이 bad 로 시작하면 거부, 아니면 성공

마지막 줄이 이 작업의 핵심 함정이다.

마커가 없는 파일에서는 bad 접두사가 기대값 그 자체다

마커 없는 bad_ari.fearity.fe 로 바꾸면 러너는 그 순간부터 성공하기를 기대한다. 그리고 그 fixture 는 실패한다.

마커가 있는 파일은 마커가 기대를 정하므로 접두사가 아무 의미도 없다. 마음대로 지어도 된다.

그래서 이 핸드오프는 마커 없는 37개를 먼저 처리한다.


착수 전 기준선

uv run python tests/run.py
→ 150/188 passed (58 pin a line and message)

두 숫자 모두 끝까지 변하면 안 된다.

  • 줄면 무언가 깨진 것이다
  • 늘면 검사를 약화시킨 것이다. 이쪽이 더 나쁘다. 조용히 통과하는 테스트는 없는 테스트보다 해롭다

어느 쪽이든 되돌리고 보고하라.


1. 마커 없는 fixture 37개 — 증거만 모은다

이 37개는 마커가 없어서 "거부되기만 하면 통과" 다. 엉뚱한 이유로 거부돼도 초록이다. 마커를 붙여야 하는데 그 판정은 네가 하지 않는다.

types/    19   bad_ari bad_asgn bad_cast bad_cond bad_mlet bad_ret bad_shwr
               bad_type bad_unit bad_unk bad_void badarr badchar badcycle
               badfield badfld badindex badmat badstr

format/   10   bad_ari bad_bufw bad_cls bad_many bad_open bad_run bad_try
               bad_type bad_verb bad_writ

own/       8   bad_clos bad_cond bad_dbl bad_dest bad_drop bad_loop bad_move
               bad_proj

왜 판정을 맡기지 않는가

판정 결과가 세 갈래로 갈리는데 그중 하나는 컴파일러를 고쳐야 하는 경우다.

결론 조치
마커를 안 붙였을 뿐, 진단은 옳다 마커를 쓴다
진단은 나오지만 다른 이유로 거부하고 있다 fixture 를 다시 본다
진단이 부실하다 — 규칙 위반을 못 짚고 뭉뚱그린 오류만 낸다 컴파일러를 고친다

세 번째가 실제로 있었다. own/badweak.fe 는 mut 대여를 shared 로 약화시키는 것을 검사하는데 컴파일러는 type mismatch 라고만 했다. 실제 출력을 마커에 그대로 베꼈다면 초록이 되면서 컴파일러의 부실한 진단이 정답으로 굳었을 것이다.

그래서 너는 증거를 모으고 판정은 사람이 한다.

파일마다 보고할 것

파일        fec/tests/types/bad_ari.fe
검사 대상   이 코드가 무엇을 위반하려 하는가 — 네가 읽고 판단한 것
근거        그렇게 본 이유. 어느 줄의 무엇 때문인지
실제 진단   .build\fec.exe --check <경로> 의 출력 전문 (줄·열·문구 그대로)
일치 여부   실제 진단이 '검사 대상' 을 짚는가 — 예 / 아니오 / 애매

일치 여부 가 이 작업의 산출물이다. 나머지는 그 판단의 근거다. 애매하면 애매하다고 써라. 억지로 '예' 로 만들면 이 작업이 무의미해진다.

절대 금지

  • // ERROR: 마커를 하나도 쓰지 마라. 이 항목의 산출물은 보고서뿐이다
  • fec/src/ 의 어떤 파일도 고치지 마라
  • tests/run.py 를 고치지 마라 (읽는 건 권장)
  • fixture 의 내용을 고치지 마라
  • 실제 출력을 그대로 마커로 옮기는 것 — 가장 하기 쉽고 가장 해로운 실수다

산출물

리포지터리 루트에 fixture-report.md. 디렉터리별로 나누고 위 다섯 항목을 담는다. 이것만 커밋한다.


2. fixture 이름 짓기

1번을 끝내고 보고한 뒤에 시작한다. 1번의 판정 결과가 이름을 바꾸기 때문이다.

대상은 네 디렉터리의 모든 .fe 파일이다.

fec/tests/types/      31
fec/tests/format/     13
fec/tests/own/        50
fec/tests/optional/   28

units/ generic/ parse/ pending-backend/제외한다. (units/generic/ 은 이름이 import 경로의 일부라 구조가 다르다.)

이미 제대로 된 이름 셋은 손대지 마라.

own/globalm.fe   own/localesc.fe   own/self_fld.fe

이름 제약 (컴파일러가 강제한다)

.feunit <이름>; 을 갖고 그 이름이 파일명(확장자 제외)과 정확히 같아야 한다. 이름은 소문자로 시작, a-z0-9_ 만, 최대 8자. DOS 8.3 에서 온 제약이고 SPEC.md §8.1 의 일부라 바꿀 수 없다.

디렉터리가 다르면 이름이 겹쳐도 된다

지금 types/bad_ari.feformat/bad_ari.fe동시에 존재하고 테스트는 통과한다. 각 fixture 는 독립된 빌드다. 지난번 실패는 이걸 몰라서 억지로 유일하게 만들려다 숫자를 붙인 것이다. 각 디렉터리 안에서만 유일하면 된다.

접두사

마커가 있는 파일 bad/ok 접두사를 버려도 된다. 기대는 마커가 정한다. 8자를 접두사에 쓰지 마라
1번의 37개 사람이 마커를 붙이기 전까지 bad 접두사를 반드시 유지해야 한다. 남는 건 5자다

37개는 5자 안에 뜻을 담기 어려우니 가능한 만큼만 개선하고, 안 되는 건 그대로 두고 목록에 적어라. 마커가 붙으면 그때 다시 짓는다.

절차

파일 하나마다:

  1. 연다. 전체를 읽는다. 대개 10줄 미만이다
  2. 무엇을 검사하는지 판단한다. 마커가 있으면 강한 단서다
  3. 그것을 8자 안에 나타내는 이름을 짓는다
  4. git mv 로 옮긴다
  5. 파일 안 unit 선언을 새 이름으로 고친다. 안 하면 컴파일러가 거부한다
  6. 그 외에는 파일을 한 글자도 건드리지 마라

이름의 기준

이름은 "무엇을 검사하는가" 를 나타낸다.

좋음:   badgmut  → globalm      전역을 mut 로 빌리는 것
        badlocsl → localesc     지역 변수의 참조가 탈출하는 것
        badfld   → reffield     구조체 필드에 참조를 둔 것      (own/)
        badfld   → nofield      없는 필드에 접근한 것           (types/)

나쁨:   badarr   → ba1          아무것도 말하지 않음
        badweak  → badweak2     숫자는 정보가 아님
        badcatch → badcatc      그냥 자른 것

축약은 해도 된다. 다만 읽어서 짐작이 가야 한다.

절대 금지

  • 첫 줄 // ERROR: 마커를 만들거나 고치거나 지우지 마라
  • unit 선언 외의 내용 변경
  • 빈 줄 추가 — 지난번에 119개 파일에 군더더기 빈 줄이 들어갔다

진행 방법

디렉터리 하나씩 끝내고 -k <디렉터리> 로 확인한 뒤 커밋해라. 범위가 좁아야 문제를 찾는다.


커밋

1번은 하나, 2번은 디렉터리마다 하나. 메시지는 무엇을 왜 바꿨는지 한국어로. 푸시하지 마라.

최종 보고

  • 각 커밋 전후의 두 숫자 (N/188, M pin)
  • 1번: fixture-report.md 경로, 그리고 일치 여부: 아니오 / 애매 로 판정한 것의 목록. 거기가 컴파일러를 고쳐야 할 수도 있는 지점이라 가장 중요하다
  • 2번: 바꾼 이름 전체 목록디렉터리/이전 → 이후 와 각각 한 줄 근거. 근거가 안 써지는 이름은 잘못 지은 것이다
  • 2번에서 이름을 못 지은 파일 목록과 이유. 억지로 짓지 말고 남겨라
  • 판단이 필요해서 건너뛴 것