# Stool / Drawer: native-session policy harness 평가

**Harness 구현과 평가를 완료했지만, 이번 후보들은 stool과 drawer를 해결하지 못했다.**

| Task | 개발 round_00 | 개발 round_01 | 고정 정책 최종 성공 | 종료 시 전체 pose 일치 | 실행 오류 |
|---|---:|---:|---:|---:|---:|
| stool | 0/3 | 0/3 | **0/10** | 0/10 | 0/10 |
| drawer | 0/3 | 0/3 | **0/10** | 0/10 | 0/10 |

최종 20회 모두 2500 step을 실행했다. 누적 조립 판정을 받은 pair도 stool 0/30, drawer 0/20이었다. 10회만으로 불가능성을 증명하지는 못한다(각 성공률의 95% Wilson 구간 0–27.8%). 다만 현재 후보가 잘 동작한다는 근거는 없다.

확정 원자료: [수치 보고서](report.md), [stool 결과](stool/result.json), [drawer 결과](drawer/result.json).

## 무엇을 만들었나

현재 코딩 세션이 Python policy를 작성하고, harness가 코드 검사 → 독립 sbatch 평가 → 로그·영상 수집 → 수정 요청 작성 → 후보 선택 → 코드 고정 → 별도 초기 상태 검증을 관리한다. `Policy(spec).act(obs)`는 관측에 따라 EE 목표 자세와 gripper 명령을 내는 상태 있는 정책이다. 실행 도중 LLM 호출은 없다.

API 없는 **현재 세션 기반 개발 루프**다. 세션 없이 CLI만 실행하면 새 정책을 스스로 작성하지는 않는다. `needs_response` 상태의 요청을 세션이 읽고 `response.md`를 작성해야 한다. 일단 생성된 정책의 실행과 평가에는 세션이나 LLM이 필요 없다.

## 비용과 출처

사용자의 API 금지 요청 이후 별도 LLM API·외부 agent CLI 호출은 없다. 금지 요청 전 v1에서 완료된 생성 요청은 stool 1건, drawer 1건이다. 해당 controller는 종료했고 현행 `run.py`에는 API backend가 없다. v4 초기 후보도 그 코드를 현재 세션에서 수정한 후손이다. 따라서 이번 결과를 “처음부터 API 산출물이 전혀 없는 독립 생성 실험”으로 설명하지 않는다. 상세 출처는 `ancestry.json`에 남겼다.

모든 시뮬레이션은 GPU 1개짜리 sbatch에서 실행했다. 로그인 노드에서 평가하거나 실행 중 worker에 원격 명령을 보내지 않았다.

## 실험 조건과 해석 범위

- simulator의 정확한 부품 pose와 평가 설정에서 가져온 조립 목표 transform을 정책에 제공했다. 이 정보 특권을 가진 조건에서도 가능한지 보는 시험이다. RGB-only, real-faithful CaP-X 또는 기존 제한된 CaP-X와 공정한 직접 비교가 아니다.
- 개발 seed 3101–3103, 최종 seed 8101–8110, episode당 2500 step. `low` randomness의 같은 가구 형상에서 초기 상태만 바꾼다.
- v4는 2개 후보/task를 평가했다. 그 전 v1–v3 개발에서 얻은 경험과 코드가 이어졌으므로 전체 연구 비용이 2회 수정뿐이었다는 뜻은 아니다.
- v4 두 후보는 양쪽 task 모두 개발 0/3, 조립 판정 부품 0개, 코드 실행 오류 0건이었다. 정해 둔 동점 기준인 최종 위치 오차 합으로 양쪽 round_01을 선택했다. 이것은 성공 후보라는 뜻이 아니다.
- 공식 성공 판정은 한번 맞은 조립 pair를 누적 보존한다. 별도로 종료 시점 모든 pair의 동시 pose 일치 여부를 기록했다. 둘 다 손을 놓은 뒤 장시간 안정성을 보증하지 않는다.
- 같은 개발 seed의 초기 pose를 비교하면 round 간 최대 차이는 위치 0.18mm 미만, 회전 0.20° 미만이었다. seed는 고정했지만 물리 상태가 bitwise 동일한 재현은 아니다(`development_reset_comparison.json`).
- 최종 검증을 시작한 뒤 정책을 수정하지 않았다. 이 seed의 결과로 다음 버전을 고치면 다음에는 새로운 최종 검증 seed를 써야 한다.

## 개발 중 고친 것과 남은 실패

### 공통 실행 harness

초기 EE 목표를 매 tick 측정 위치에서 조금씩만 이동시키는 방식은 접촉/추종 오차에 막혀 하강이 정체됐다. 후보 내부 목표 reference를 독립적으로 전진하도록 바꿨다. 이후 기존 scripted controller보다 작았던 adapter 이동 제한을 x/y 11cm, z 7cm의 기존 reference 범위로 맞췄다. 이 공통 변경은 v4라는 새 실험으로 분리하고 후보들을 다시 평가했다. 회전 delta의 곱 순서와 유한값·범위를 단위 테스트로 확인했다.

### Stool

개발 영상·trace에서 다리를 집어 옮기고 조립 목표 근처까지 내리는 동작은 관찰됐다. v4 round_00은 약 6.4mm 오차 근처에서 더 밀다가 다시 빼내는 실패가 있었다. round_01은 이 구간에서 손을 놓고 조임 단계로 전환하고, 가까이 놓인 다리를 우선 처리하도록 수정했다.

조임 단계에 진입하는 개선은 있었지만 조립 성공으로 이어지지 않았다. seed 3102에서는 그리퍼 폭 약 35mm로 다리를 잡고 여러 차례 회전했어도 위치 오차가 대체로 6.3–6.5mm에 남았다. 다른 seed에서는 회전 이후 자세 오차가 커졌다. 이는 조임 명령을 보냈다는 사실과 실제 부품의 축 방향 진전·정렬을 구분해야 함을 보여준다. 나사 방향 하나만 바꾸면 해결된다는 증거는 없다.

근거: `stool/round_00/dev/seed_3101.trace.jsonl`, `stool/round_01/dev/seed_3102.trace.jsonl`, 각 개발 영상.

### Drawer

전면 lip grasp로 변경한 뒤 서랍을 잡고 들어 회전·운반하는 동작은 관찰됐다. 그러나 입구에서 삽입할 때 캐비닛이 움직였다. v4 round_01 seed 3101은 step 274에 insert로 들어가고 282에 cabinet_contact_abort로 빠졌다. 충돌 중 캐비닛 위치를 계속 쫓아 밀지 않도록 중단 조건을 넣었지만, 이후 물체를 놓치고 재접근을 반복하는 문제까지 해결하지는 못했다.

근거: `drawer/round_01/dev/seed_3101.trace.jsonl`, `frame_0250.jpg`, `first_episode.mp4`.

## 환경 자체가 불가능한가

이미 조립된 초기 상태에서 30 step을 둔 별도 진단에서는 stool 3개 pair, drawer 2개 pair가 모두 계속 현재 일치 상태를 유지했다. 결과는 상위 폴더의 `stool_fixture.json`, `drawer_fixture.json`이며 job은 125477/125478이다.

이것은 정지된 조립 상태가 시뮬레이터 안에 존재한다는 제한적인 근거다. 로봇이 분리된 부품에서 그 상태까지 도달할 수 있다는 증명도, policy 성공 횟수도 아니다. 조립된 reset의 mutable transform 문제를 피하려고 생성 직후 unwrapped environment를 step하는 진단을 사용했다. 처음 진단 두 건의 Gym wrapper 오류는 정책 실패와 분리해 기록했다.

## 다음에 손볼 우선순위

1. **조작 단계를 독립 검증 가능한 primitive로 분리한다.** grasp → lift → transport → preinsert → insert/thread → release마다 실제 부품 움직임으로 성공 조건을 확인한다. 전체 episode 성공 0/1만 보고 긴 코드를 다시 쓰면 어느 수정이 좋아졌는지 알기 어렵다. 부분 단계 평가는 진단으로 기록하고 전체 task 성공과 분리한다.
2. **Stool은 접촉 자세와 조임 진전을 먼저 해결한다.** one-leg에서 검증한 grasp/축 정렬/손목 풀기 로직을 재사용 가능한 형태로 추출하되 stool 형상에 맞게 검증한다. 부품 상대 회전과 축 방향 이동이 실제로 생기는지 확인하고, 진전 없는 조임 반복은 중단한다. 다리 1개의 삽입·release 안정성을 확보한 뒤 3개 slot 순서를 결합한다.
3. **Drawer는 손가락까지 포함한 충돌 여유와 재그립을 다룬다.** tray pose만 정렬해도 gripper가 입구에 걸릴 수 있다. 전면 grip의 깊이·접근 각도·부분 삽입 후 release 위치를 함께 설계하고, 필요하면 재그립/외부 밀기 단계로 넘긴다. 캐비닛을 크게 움직이기 전에 접촉을 감지해야 하며, 떨어뜨린 tray의 자세에 맞는 회복 경로가 필요하다.
4. **실패가 반복되면 진전 없는 후보를 빠르게 탈락시킨다.** 동일 조건 개발 비교, 오류 유형별 trace, primitive 성공률을 후보 선택의 진단 근거로 쓴다. 최종 성공 판정이나 simulator 물리·초기화는 바꾸지 않는다.

코드 생성·수정 harness 형태는 구현할 수 있다. 다만 이 범위의 실험은 one-leg에서 통했던 서술형 노하우를 다른 접촉 조립 task에 옮기는 것만으로 해결된다는 증거를 주지 못했다. 다음 개선의 중심은 LLM 호출 횟수 증가가 아니라 검증된 조작 primitive와 실제 접촉 후조건이어야 한다.


## 검증과 재실행

평가 job 125508(stool), 125500(drawer)는 모두 `COMPLETED`, exit code `0:0`이다. 결과 수집 시 정책 hash·seed·episode 수, benchmark hash, 실행 harness source hash를 확인했다. 상세 증거는 `verification_before_maintenance.json`에 있다.

평가 종료 후 두 운영 오류만 수정했다. `run.py`는 Slurm active 목록에서 사라진 ID를 회계 기록으로 확인하며, 완료된 실험 결과는 이후 코드가 바뀌어도 조회할 수 있다. `check_fixture.py`에는 이미 별도 진단에서 검증한 `core.step` 수정을 반영했다. 평가기·servo·고정 정책·benchmark는 바꾸지 않았다. 변경 내역과 hash는 `post_run_maintenance.json`에 남겼다.

단위 테스트 **17개 통과**, 두 shell wrapper 문법 검사 통과, 실제 종료 job 조회와 완료 실험 재조회 통과. 생성·수정·평가 명령과 파일 구조는 [harness 사용법](../../../scripts/policy_harness/README.md)에 정리했다. 코드 snapshot, 모든 후보, 실패 영상과 trace를 보존했다.
