P0 수정 독립 재검증 — 앱상주서버(한선)/crowny-browser.m(ObjC)
개요
벡터직결 계약(2026-07-12) 부록2 P0-1(한선, 스크롤백 꼬리파싱)과 P0-2/3/4(ObjC, 크래시재기동·종료고아·실좌표클릭)에 대해 담당A/담당C가 각각 완료 보고한 내용을 독립 재검증했다. 요청된 6개 항목 중 5개는 통과,
② 대형 스크롤백 키 RTT(p50<10ms)는 실패(p50 ≈ 57ms, 목표의 5.7배)로 확인되어 종합 target_met = false.
무엇을 검증했는지 (모두 별도 인스턴스, 운영 CrownyAI.app(PID 83062)은 무접촉)
- 컴파일 재검증:
CROWNY_STRICT=1 hanseonc_high 앱상주서버.한선 exit0/경고0. ObjC 두 빌드플래그 조합 모두 syntax+link exit0, 신규경고 0(기존 unused-function 2건만).
- ① 스트레스 2500회: 3001줄 스크롤백(278KB) 고정, 격리 포트(28901)에서 순수 소켓으로 2500회(터미널/계산기 혼합) → 0 errors, p50=52.40ms, drift 1.01x(열화없음), 프로세스 생존, [STR] 90% 경고 1회(기지 잔여 이슈, 크래시 아님). 종료 verb로 정상 종료(누적요청=2501 카운터 정합).
- ② 대형 스크롤백 GUI 키 RTT N=20 (신규 발견 — 목표 미달): 동일 3001줄 스크롤백을 로드한 실제 ObjC 앱(격리 인스턴스) 터미널 탭에 AUTOSEQ로 21회 실키 입력 →
[CB_VEC_RTT] event=KEY 로그 n=17, p50=57.2ms, min=56.5ms, max=58.4ms — 목표(<10ms)의 약 5.7배. 원인 규명: 소켓만 격리 측정 시 소형(10줄) 스크롤백 p50=3.67ms vs 동일 3001줄 스크롤백 p50=52.54ms(14배). _출력줄들()/_터미널경로헤드()가 각각 버퍼파일읽기()로 전체 파일을 읽는 것 자체가 O(파일크기)이고, 꼬리스캔(버퍼바이트읽기 역순)은 파싱 단계만 O(30줄)로 줄였을 뿐 — P0-1의 "문자열 핸들 소비 요청당 상수" 주장은 크래시 방지 목적으로는 유효하나, "글리프 드로우 비용"이 아니라 전체 파일 읽기 비용이 스크롤백 크기에 비례해 지연시간 목표를 깬다.
- ③ 자식 kill → 재기동: 계산기 탭 appSrv 자식을 SIGKILL(크래시 후 1.6초 시점) → 로그 "종료 감지(status=9)" → "다운 감지 — 1회 재기동 시도" → "재기동 성공 port=9803" 확인, 이후 클릭 정상 동작. 부수 발견: 재기동을 유발한 바로 그 클릭 1회는 조용히 유실됨(VEC_MISMATCH 기대5/실측4, 스크린샷 픽셀 대조로 첫 클릭 "7" 누락 확정) — 서버는 회복하지만 트리거 이벤트 자체는 재시도되지 않는 좁은 레이스. 크래시는 없음(치명 아님), 다만 미문서화 잔여.
- ④ AUTOSEQ 종료 3회+ 고아0: 계획한 3회(A/B3/C) 외 우발적 2회(계산기 5클릭 2건) 포함 총 5회 AUTOSEQ 실행 전부 자기종료, 9801-9820 리스너 diff 0(신규 고아 없음) 확인.
- ⑤ 실좌표 클릭 5회: 계산기 7/8/+/=/C 버튼 실좌표 클릭 5회, CB_VEC_RTT event=액션 5회(2.5~4.4ms) 전부 확인 + 스크린샷 픽셀로 156→0 상태전이 검증(클릭이 올바른 버튼에 정확히 맞았음을 행동으로 증명).
- ⑥ selftest 19/19 + vector PASS + 신규 발견 오프바이오션 리크:
--selftest-inputv2 19/19, --selftest-vector PASS 확인. 단, --selftest-inputv2 실행 자체가 매번 진짜 crownyc 자식(고아, ppid=1)을 하나 남긴다(termSelectLineIndex→renderAndReload→sendVectorEvent→gAppSrvPort<=0→appSrvRestartIfDown()가 실제 NSTask를 스폰하지만 이 테스트 경로는 종료 시 appSrvStop()을 호출하지 않음). 즉석에서 재현(pid 12775, port 9803) → 직접 kill로 정리. 환경에 이미 있던 미상 고아 2건(pid 93820=1h22m 전, 관련無로 추정 / pid 99334=검증 시작 6분 전, 이 리크와 정황상 부합)도 발견— 손대지 않고 기록만.
관련 파일
/Users/ef/CrownyBrowser/src/앱상주서버.한선 (읽기만, 수정 없음)
/Users/ef/CrownyBrowser/native/crowny-browser.m (읽기만, 수정 없음)
- 검증 스크립트/로그/스크린샷:
/private/tmp/claude-501/-Users-ef/2e95e8b6-e663-4b81-9734-9ded4339b18d/scratchpad/verify2/
잔여 이슈 (다음 세션 인계)
- [P1] 스크롤백 렌더 지연시간:
_출력줄들()+_터미널경로헤드()의 버퍼파일읽기() 전체읽기를 파일크기와 무관하게 만들어야 함(예: OS 레벨 seek+마지막 N바이트만 읽는 빌트인 필요, 또는 파일 자체를 append-capped 링버퍼로 운용해 "경로" 갱신 시 앞부분을 잘라내는 방식). 3001줄(278KB) 기준 p50 57ms는 10ms 목표 위반.
- [P2] 크래시 재기동 트리거 이벤트 유실:
sendVectorEvent에서 appSrvRestartIfDown() 성공 후 즉시 같은 호출 안에서 appSrvVectorRequest를 재시도하지 않고 1회 실패로 끝남 — 재기동 성공 시 그 요청 자체를 1회 재시도하면 해소 가능.
- [P2]
--selftest-inputv2 고아 리크: 테스트 종료 전 appSrvStop() 호출 추가 필요(순수 유닛테스트인데 실제 프로세스를 스폰하는 것 자체가 설계 의도와 어긋남 — termSelectLineIndex 테스트 케이스가 CrownyAppView 전체를 통해 renderAndReload를 태우는 대신 순수 함수만 검증하도록 격리하는 것도 대안).
- 환경에 이미 떠 있던 미상 고아 2건(93820 port 9801, 99334 port 9802)은 이번 세션 소관 밖이라 보존(kill 안 함) — 다음 세션이 필요시 확인.