Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ flowchart LR
- **대상과 유예:** 우편 ID가 있고 읽음 미확인인 편지를 발송 시각부터 5분 유예한 뒤 현재 수신 역할별로 묶는다. 카드 없는 질문도 우편 알림 대상이지만 작업 감시AI 대상이 되는 것은 아니다. 명시적으로 연결된 업무가 보류·완료·취소이면 제외한다. 별도의 판 활성 상태를 추측하지 않는다.
- **전달 전 확인:** 기존 start 원장과 실제 세션 PID가 맞아야 한다. 예약 뒤 읽음·인계·업무 상태와 생존을 다시 확인한다. 사용자·종료 역할을 기동하지 않으며 원문을 재전송하지 않는다.
- **재시작 중복 방지:** 시스템 원장의 `watch-mail-reminder`에 우편 ID와 현재 수신 역할별 예약(`reserved`)·결과(`result`)를 남긴다. 같은 우편·같은 수신 역할은 한 번만 알린다. 예약만 남았거나 실패·전달 불명확이어도 재시작으로 자동 재전송하지 않는다. 후임으로 책임이 이전되면 그 후임에 대한 알림은 별도 한 번 가능하다.
- **사람 입력 보호(2026-09-23):** 알림 예약 전에 tmux 바닥에서 창에 붙은 사람의 마지막 키 입력(`client_activity`)이 30초 안이거나 입력창에 글이 있으면 보류한다. 보류는 붙여넣기 전이므로 예약하지 않고 `watch-mail-held`를 사유별로 한 번만 남긴 뒤 다음 순회에 다시 본다. 확인과 붙여넣기 사이에 입력이 생겨 전송 검사에서 멈춘 경우에는 `result`에 `held:true`를 남기고 예약을 풀어 재시작 뒤에도 다시 시도한다. 입력창 판별은 카드 전송과 같은 기준이다. rottie 바닥은 이 보류를 지원하지 않는다.
- **사람 입력 보호(2026-09-23):** 알림 예약 전에 tmux 바닥에서 창에 붙은 사람의 마지막 키 입력(`client_activity`)이 30초 안이면 보류한다(2026-10-17부터 입력창 글 모양은 보지 않는다). 보류는 붙여넣기 전이므로 예약하지 않고 `watch-mail-held`를 사유별로 한 번만 남긴 뒤 다음 순회에 다시 본다. 확인과 붙여넣기 사이에 입력이 생겨 전송 검사에서 멈춘 경우에는 `result`에 `held:true`를 남기고 예약을 풀어 재시작 뒤에도 다시 시도한다. 카드 전송·일반 우편도 같은 기준을 쓴다. rottie 바닥은 이 보류를 지원하지 않는다.
- **재귀 제외:** 확인 알림 자체와 기록 전용 자동 완료 우편은 `notificationOnly: true`로 추가 미확인 알림에서 제외한다. 이 필드가 없던 과거 `systemGenerated: task-completion`도 제외한다. 읽음 기록은 자동으로 만들지 않고 원장에 그대로 두되, `inbox --unread`와 화면의 미확인 건수·필터에서는 뺀다(2026-09-27 [kyle]). 전체 목록에서는 **기록용 · 추가 알림 없음**으로 함께 표시한다.
- **질문 대기:** 읽음·중간 답장으로 답변 대기를 끝내지 않는다. 현재 수신 책임자의 최종 답변 또는 현재 발신 책임자의 취소로 종료한다. 새로운 답변 지연 자동 상신은 없다.

Expand Down
6 changes: 3 additions & 3 deletions docs/automatic-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ ready/assigned/done 상태만 가능하다. 원본 본문을 복제하지 않고
확인한다. 프로그램은 역할 생성, 모델 변경, 권한 확대, 시한 연장을 하지 않는다.
같은 업무의 다른 실행이나 다른 담당이 두 역할을 점유하고 있으면 설정·전달을 막는다.

자동 발령에도 일반 카드 발령과 같은 실제 AI 프로세스·모델·빈 입력창 검사가 적용된다.
등록된 실행기라도 화면의 안내 문구·테두리·기존 출력 때문에 빈 입력 영역을 확정할 수
없으면 전송 전에 `exception`으로 멈춘다. 실제 Devin 화면에서도 이 제한을 확인했으므로,
자동 발령에도 일반 카드 발령과 같은 실제 AI 프로세스·모델·사람 최근 입력(30초) 검사가 적용된다.
사람이 그 창에서 최근 30초 안에 키를 눌렀으면 전송 전에 `exception`으로 멈춘다.
화면의 안내 문구·테두리·기존 출력으로 빈 입력 영역을 판정하지 않는다(2026-10-17).
지원 명령 목록이나 격리 시험 통과만으로 실제 UI의 자동 왕복이 검증됐다고 보지 않는다.

```sh
Expand Down
23 changes: 14 additions & 9 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,20 @@ tmux는 (a)(b)(c)를 충족했고 (d)는 Mac에서 통과했다(서버 왕복은
tmux 카드 전송은 버퍼 준비 전과 붙여넣기 직전에 `pane_dead`, 같은 pane PID,
입력을 받는 전경 프로세스 그룹에 속한 pane 프로세스/자식의 실행 파일 또는
Node/Bun 스크립트 이름을 대조한다. 뒤에서 실행 중인 AI만으로는 허용하지 않는다.
입력 줄은 커서가 있는 프롬프트 줄(`›`·`>`·`❯`·devin `❭`) 하나다. 화면 전체의 프롬프트 모양 줄을
후보로 세지 않는다 — codex 배너(`│ >_`)·과거 프롬프트 때문에 실제 대기 화면을 거부한 사고(2026-09-23).
그 위쪽 가장 가까운 글 줄도 프롬프트 모양이면 여러 줄 입력과 구별할 수 없어 보류한다.
입력 상자 윗줄은 0열에서 pane 폭을 채우는 가로줄(표지 허용)이며, 입력 상자(devin·claude)는 바로 아래 닫는 가로줄 밑을 상태줄로 보고, 상자가 없으면(codex) 빈 줄 뒤 상태줄 한 줄만 허용한다. 단 상태줄에 ` · `가 있고 바로 다음 줄이 단축키 안내(`? for shortcuts` 포함, codex 0.163.0 두 줄 바닥글)이면 그 한 줄까지 허용한다.
프롬프트 뒤 글은 실제 화면에서 확인한 빈 입력 자리표시자만 빈 입력으로 본다(작업 중 자리표시자는 제외).
입력칸 글이 모두 흐린 글씨(SGR 2, `capture-pane -e`)면 실행기가 띄운 예시·다음 입력 추천으로 보고 빈 입력으로 본다(claude-code의 `Try "…"`와 작업 뒤 추천, 2026-09-23 실측). 흐리지 않은 글이 한 글자라도 있으면 사람이 친 글로 보고 보류한다.
커서가 프롬프트 줄 밖이거나 이 모양과 다르면 보류한다. 프롬프트가 없거나 미제출 텍스트가 있거나 빈 입력창을 확인하지
못하면 전송하지 않는다. 이는 화면에 보이는 빈 입력의 확인이며 AI 인증·모델 응답
성공을 증명하지 않는다. 복사모드 검사는 기존 공통 전송 경계를 재사용한다.
사람 보호는 화면 모양이 아니라 **창에 붙은 사람의 최근 키 입력**(tmux `client_activity`) 하나다(2026-10-17 [kyle] 결정).
카드 전송·감시 알림·일반 우편이 같은 기준을 쓴다. 붙은 사람의 마지막 키 입력이 30초(`HUMAN_IDLE_MS`) 안이면
`KADAN_HUMAN_ACTIVE`로 보류하고, 값이 숫자가 아니면 `KADAN_HUMAN_ACTIVITY_UNKNOWN`으로 보류한다(실패 닫힘).
붙은 client가 없으면(백그라운드 역할) 통과한다. 우회는 감시기의 5번째 강제 차례(`force`)뿐이다.
일반 우편이 막히면 `not-sent`로 실패하며 새 대기열은 만들지 않는다(슈퍼 간 우편만 기존대로 보관).
프롬프트 줄·자리표시자·입력 상자 테두리·바닥글 모양으로 빈 입력창을 판정하던 장치(`checkEmptyInput`)는 없앴다.
실행기 업데이트로 화면 모양이 바뀌면 빈 입력창을 미제출 글로 오판해 발령과 알림이 통째로 막혔기 때문이다
(2026-09-23 devin·claude, 10-11 herdr 폭, 10-17 codex 0.163).
**받아들인 손해:** 사람이 글을 치다 30초 넘게 손을 떼고 그대로 두면 그 글 뒤에 우편이 붙어 제출될 수 있다.
**붙기만 한 창은 사람 입력으로 보지 않는다:** tmux는 client가 붙는 순간 `client_activity`를 `client_created`와 같게 두고,
크기 바꾸기·`paste-buffer`·`send-keys`로는 바뀌지 않으며 그 client의 키 입력으로만 바뀐다(제어 모드 client는 명령을 보내도 안 바뀐다, 2026-10-17 격리 소켓 실측).
그래서 활동이 붙은 시각보다 뒤일 때(`activity > created`)만 사람 입력으로 센다. 로티 탭이 시작과 동시에 붙어도 첫 안내·발령이 막히지 않는다.
한계: 붙은 바로 그 초 안에 누른 키는 놓친다. 반대로 붙은 뒤 터미널 응답 같은 입력으로 활동이 바뀌면 그 창은 30초 보류된다(막히는 것은 시작 첫 안내뿐이고 start는 성공).
이는 화면에 보이는 확인이 아니며 AI 인증·모델 응답 성공을 증명하지 않는다. 복사모드 검사는 기존 공통 전송 경계를 재사용한다.
이 tmux 프로세스·입력 검증은 Rottie 바닥의 실측을 대신하지 않는다.
실제 전경 AI 프로세스의 명시 모델도 시작 기록과 대조한다. 불일치·미확인이면 거부한다.
빈 셸·비AI 명령·모델 미지정·이전 세대 신원만 남은 pane에는 카드를 붙여넣지
Expand Down
18 changes: 13 additions & 5 deletions docs/experiments/herdr-roundtrip.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
- `KADAN_HOME`과 절대경로 `KADAN_HERDR_SOCKET`을 명시한다. 자동 서버 기동·설치·로그인·훅 설치·다른 바닥으로 폴백하지 않는다.
- `cwd`를 생성 요청에 지정한다. 시작 원장에 서버 주소, workspace/pane/terminal ID와 실제 시작 PID를 기록한다. 한글 역할 이름은 그대로 유지한다.
- 주소 조회는 원장의 terminal ID를 기준으로 한다. 반환된 pane/tab ID를 사용하며 번호를 추측하지 않는다.
- 전송 전 현재 PID·foreground 실행기·명시 모델·입력 대기·미제출 입력·스크롤 상태를 확인한다. 명령 인자가 생략된 경우 해당 로컬 foreground PID만 `ps`로 조회한다.
- 전송 전 현재 PID·foreground 실행기·명시 모델·입력 대기·스크롤 상태를 확인한다. 미제출 글 모양은 판정하지 않는다(2026-10-17, 아래 "입력창 판정 제거"). 명령 인자가 생략된 경우 해당 로컬 foreground PID만 `ps`로 조회한다.
- `agent.prompt`로 괄호 붙여넣기와 Enter를 제출한다. 전달 성공은 `keyDelivery=sent`, `inputAcceptance=unconfirmed`이며 실행 완료를 뜻하지 않는다.
- 입력창 검사는 읽기 전용 terminal observer와 visible snapshot을 사용한다. `takeover`는 사용하지 않는다.
- 화면 확인은 `pane.read`의 visible text를 쓴다. `takeover`는 사용하지 않는다.
- 화면 확인에 `terminal session control`·`terminal attach`를 쓰지 않는다. 실제 창 크기를 흉내 내려고 백그라운드로 띄우는 것도 금지다. 2026-10-12 시험(HW-6) 중 뒤에 남은 control 클라이언트 3개가 각각 68~130GB(압축 포함)까지 불어나 맥 전체가 메모리 부족에 걸렸다(01:36~07:53, 8회). 격리 재현 8가지 조건에서는 재현되지 않아 원인은 미확정이며 herdr에 보고했다(herdrdev/herdr#5175). 행 수가 필요하면 `observe --rows <pane.scroll.viewport_rows>`를 쓴다.
- 응답 유실은 전달 불명확으로 끝낸다. 본문/Enter 자동 재전송은 없다. 자동 알림은 빈 입력창·에이전트 대기·동일 화면이 30초 이상 관측된 뒤 전송 직전 재검사한다. 작업 중·승인 대기·스크롤·입력 미확인은 보류한다. Enter 단독 재전송은 계속 거절한다.
- 응답 유실은 전달 불명확으로 끝낸다. 본문/Enter 자동 재전송은 없다. 자동 알림은 에이전트 대기·동일 화면이 30초 이상 관측된 뒤 전송 직전 재검사한다. 작업 중·승인 대기·스크롤·입력 미확인은 보류한다. Enter 단독 재전송은 계속 거절한다.
- 출력은 ANSI recent snapshot을 읽는다. `truncated`이면 기존 wait가 gap으로 중단한다. 완전한 출력 로그나 스트림 재생 기능을 제공하지 않는다.

## 시험 환경에서 사용하는 법
Expand Down Expand Up @@ -54,7 +54,7 @@ kadan attach 판-작업자
| AI 도구의 300줄 명령 출력 뒤 DONE | 회수 성공. CLI가 중간 출력을 접어서 보여 줌 |
| 새 DONE 없는 응답 | idle이어도 quiet 종료, done 기록 없음 |
| PID 불일치 | 입력과 send 기록 없음 |
| 미제출 입력 | 원문 보존, 새 본문 거절 |
| 미제출 입력 | (2026-10-11 당시) 원문 보존, 새 본문 거절. 2026-10-17부터 화면 모양을 판정하지 않아 카드·일반 우편은 보낸다 |
| 실제 신뢰 확인 화면의 blocked | 입력 거절 |
| 종료한 시험 터미널 | 입력 거절 |
| 연결 불가능한 시험 소켓 | 실패 보고, 자동 재시도 없음 |
Expand Down Expand Up @@ -113,11 +113,19 @@ Rottie의 각 프로젝트에 `terminal create --workspace <그 프로젝트> --
## 자동 알림 보호 보완 — 2026-10-13

일반 경보·감시 AI 보고·미확인 우편·유휴 알림이 같은 입력 보호를 쓴다.
herdr에는 최근 사람 입력 시각 API가 없어 화면 revision과 입력 줄·PID·terminal ID의 안정성을
herdr에는 최근 사람 입력 시각 API가 없어 화면 revision과 화면 글·PID·terminal ID의 안정성을
함께 확인한다. 감시기 재시작이나 관측 실패 뒤에는 30초를 새로 센다.
보호로 확실히 미전송된 경우만 나중에 재확인하며, 이미 해소된 경보는 보내지 않는다.
응답 유실이나 발송 도중 중단은 자동 재전송하지 않는다.

화면 관측과 `agent.prompt` 사이를 서버가 원자적으로 잠그지는 못한다. 마지막 검사 이후에
사람이 키를 누르는 짧은 경합 구간까지 완전히 없앤 보장은 아니다. 2026-10-13 실제 Codex 두 세션으로 입력 문장 보존, 30초 유예, 요청→자동 깨움→최종 답장→
요청자의 읽음과 양쪽 한 번씩 알림을 확인했다. 운영 세션에 적용한 증거는 아니다. 레포 간 흐름은 [슈퍼 우편](../peer-super-mail.md)을 따른다.

## 입력창 판정 제거 — 2026-10-17

- 결정: [kyle]이 화면 모양으로 빈 입력창을 판정하던 `checkEmptyInput`을 없앴다. 실행기 업데이트마다 빈 입력창을 미제출 글로 오판해 발령이 막히는 것(herdr 폭 10-11, devin·claude 9-23, codex 0.163 10-17)이 사람 글 뒤에 우편이 붙는 것보다 치명적이라고 보았다.
- 지운 것: herdr 쪽 `inputFrame`·`herdrInputFrame`(관찰 창 세로 줄 수로 커서 행을 구하던 장치)와 `checkEmptyInput` 호출. 위 "입력창 폭 판정"·"입력 커서 행" 항목은 그 당시 기록이며 이제 해당 코드가 없다.
- 방침 (b): 카드 전송과 일반 우편은 사람 검사 없이 보낸다. 이유는 (a) 화면 30초 안정 검사를 카드·우편에도 쓰려면 안정 기록을 메모리 맵(`herdrNotificationGuard`의 `quiet`)에 두는데, `kadan send`·`dispatch`는 호출마다 새 프로세스라 이전 관측을 기억하지 못해 늘 처음 관측으로 보류되기 때문이다(항상 막힘). 장수 프로세스인 감시기의 알림만 30초 안정 검사를 유지한다.
- 알림 지문: `frame.row`를 뺐다. 지문은 소켓·terminal·PID·pane·화면 revision·화면 글이다. 커서 행은 화면 글이 바뀌면 함께 바뀌므로 안정 판정에 더하는 정보가 없다.
- 받아들인 손해: herdr에서는 사람이 쓰던 글 뒤에 카드·우편이 붙어 제출될 수 있다. 승인 대기·작업 중(카드)·기록 보기 중 검사는 그대로다.
2 changes: 1 addition & 1 deletion docs/flows.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
| 큐 대기 (`큐대기`) | 발령 없는 세션 등 | 그 밖의 큐 배너·큐 블록이 5분 넘게 남음 | 직속 상위 |
| 상태 확인 필요 (`모름`) | 등록 역할·감시기 | 화면을 읽지 못함(역할), 원장을 읽지 못함(감시기) | 역할은 직속 상위, 원장 오류는 공통 수신자 |
| 감시AI 오류 (`감시AI오류`) | 감시 AI 호출 | 판정 호출이 실패·시간 초과·보고 누락(대체 후보까지 실패). 본문은 "감시기의 점검 AI가 <역할> 점검에 실패 — 무슨 일 · 대상 문제 여부는 모름 · 할 일"로 쉽게 쓴다. 인터넷 없음은 깨어 있는 동안 3번 연속일 때만, 맥북 잠자기로 끊긴 호출은 알리지 않음 | 사용자(디스코드, 안 되면 OS 알림·감시 출력). 판정 프로그램·모델 장애는 운영자 몫이라 감독에게 보내지 않는다 |
| 알림 밀림 (`알림밀림`) | 받는 창(역할) | 감시기가 보내려는 알림(미확인 우편 재알림·감시 경보·슈퍼 간 보관 우편)이 입력 보호(사람 입력·쓰다 만 글·화면 판정 불가)나 tmux 복사 모드로 3번 연속 밀림. 5번 연속이면 입력 보호를 건너뛰고(복사 모드는 해제·재확인 뒤) 넣고 원장에 `watch-notify-forced`를 남김. 전달에 성공하면 닫힘 → [입력 보호 상한](watch-overview.md#입력-보호-상한-2026-10-13) | 사용자(OS 알림). 밀리는 그 창이나 상위로는 보내지 않음 |
| 알림 밀림 (`알림밀림`) | 받는 창(역할) | 감시기가 보내려는 알림(미확인 우편 재알림·감시 경보·슈퍼 간 보관 우편)이 입력 보호(최근 30초 사람 입력)나 tmux 복사 모드로 3번 연속 밀림. 5번 연속이면 입력 보호를 건너뛰고(복사 모드는 해제·재확인 뒤) 넣고 원장에 `watch-notify-forced`를 남김. 전달에 성공하면 닫힘 → [입력 보호 상한](watch-overview.md#입력-보호-상한-2026-10-13) | 사용자(OS 알림). 밀리는 그 창이나 상위로는 보내지 않음 |
| 전달 실패 (`전달실패`) | 경보 자체 | 경보 우편을 받는 쪽 창에 넣지 못함 | 받지 못한 쪽의 상위(관계표에 없으면 `--super`) |
| 감시 제외 (`감시제외`) | 경보 자체 | 대상이 감시 범위를 벗어나 경보를 닫음(회복 편지 없음) | 기록만 |
| 자원 (`자원`) | 이 컴퓨터 | 메모리·스왑·부하 | 우편 없음. 대시보드에서 본다 |
Expand Down
4 changes: 2 additions & 2 deletions docs/kadan-guide.html
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ <h2>발령 한 번에 일어나는 일 — 결과 종류부터</h2>

<section class="panel" id="sim">
<h2>발령 검문소 — 직접 눌러 보기</h2>
<p class="note">상황을 고르고 send를 눌러 보세요. 통과한 사건만 원장에 붙고, 거부는 도장으로 남아요. 이 로직은 guardedSend·checkSend의 <b>축소 복사</b>이고, 실제 검사는 더 많은 화면 규칙(빈 입력창 모양·복사모드 등)을 본다.</p>
<p class="note">상황을 고르고 send를 눌러 보세요. 통과한 사건만 원장에 붙고, 거부는 도장으로 남아요. 이 로직은 guardedSend·checkSend의 <b>축소 복사</b>이고, 실제 검사는 더 많은 규칙(사람 최근 입력·복사모드 등)을 본다.</p>
<h3>미리 만든 장면</h3>
<div class="presets" id="sim-presets">
<button data-preset="ok">정상 발령</button>
Expand Down Expand Up @@ -669,7 +669,7 @@ <h2>규칙표 2 — 발령 검문소 순서</h2>
<tr><td>(3)</td><td>PID 대조</td><td>시작 기록의 PID와 지금 PID가 같나?</td><td><code>KADAN_PID_MISMATCH</code></td><td><code>cli.mjs:712</code></td></tr>
<tr><td>(4)</td><td>AI 신원</td><td>현재 세대가 등록 실행기·모델인가? 카드 단계 프로필과 맞나?</td><td><code>KADAN_AI_IDENTITY_UNVERIFIED</code><br><code>KADAN_STALE_RUNNER</code></td><td><code>cli.mjs:727·745</code></td></tr>
</table></div>
<p class="note">일반 우편·질문·답장은 taskId가 없으므로 (1)(4)를 거치지 않는다. tmux는 여기에 더해 빈 입력창 모양·전경 프로세스·복사모드까지 화면으로 확인한다. (cli.mjs:724, docs/design.md 규칙 2)</p>
<p class="note">일반 우편·질문·답장은 taskId가 없으므로 (1)(4)를 거치지 않는다. tmux는 여기에 더해 사람 최근 입력(30초)·전경 프로세스·복사모드를 확인한다. (cli.mjs:724, docs/design.md 규칙 2)</p>
</section>

<section class="panel">
Expand Down
Loading
Loading