Folder Bot — PRD
버전: v1.0 · 2026-09-12 · 구현 시작 (v0.1.0 테스트 빌드) · 초안 — Dave 와 함께 수정 중. 코드 작업 금지, 계획만. 이름: Folder Bot (2026-09-12 확정 · 가제 Project Bot 폐기) · 캐릭터·애칭 폴더봇. 리포는
dave-jin/folderbot(2026-09-12 개명 · 옛rondo주소는 리다이렉트). 목업: https://claude.ai/code/artifact/046fd71a-18f5-40df-980e-f7725380d2c2 (v2 — 폴더봇) 위치: 완전히 새로운 프로덕트. Rondo 알파(rondo-app)는 부품 창고로만 쓴다 (부록 A). 알파의 결정·이력은 이 문서에 적지 않는다. 1차 레퍼런스: xAI Grok Bot (2026-08 베타). 2차: Claude Code Remote Control · Codex Remote · Cursor Cloud Agents.
0. 한 줄 정의
내 Mac mini 위에서 사는, 폴더마다 하나씩 붙는 AI 동료들 — 그리고 그들을 PARA 로 관제하는 오케스트레이터.
Grok Bot 이 "봇마다 클라우드 컴퓨터 하나" 라면, Folder Bot 은 "내 컴퓨터(Mac mini) 하나에 폴더마다 봇 하나" 다. 봇을 설정하지 않는다 — **하네스가 이미 있는 PARA 폴더가 봇 후보이고, 에이전트와 함께 일할 폴더를 고르면 시작된다. 폴더를 새로 파고 시작·정지시키는 일은 사람이 직접 하거나 오케스트레이터에게 맡긴다. 어디서든(PC·iPhone·Android) 동료에게 말 걸듯 봇에게 말하고, 봇은 그 폴더의 지침·기억·자료를 들고 Mac mini 에서 일한다. 내 폰이 꺼져도 일은 계속된다.
1. 문제
- 환경이 곧 병목이다. 원격 세션(Claude Code on the web 등)은 매번 환경을 새로 만든다 — 클론·의존성·자격증명·볼트 접근. 내 Mac mini 에는 그 전부가 이미 있다.
- 세션이 폴더를 모른다. 지금은 세션을 열 때마다 사람이 폴더를 고르고 맥락을 다시 주입한다. 일은 이미 PARA 폴더로 나뉘어 있는데, 도구는 그 구조를 모른다.
- 관제탑이 없다. 프로젝트 N 개가 병행되는데 "지금 어디서 무엇이 돌고 있고, 무엇이 내 결정을 기다리나" 를 한 곳에서 못 본다.
- 책상을 떠나면 끊긴다. 이동 중·소파에서 승인 하나 누르고 한 줄 지시하는 것이 실제 사용 형태인데, 세션은 노트북 뚜껑과 운명을 같이한다.
2. 레퍼런스와 차이점
| Grok Bot | Claude Code Remote Control | Folder Bot | |
|---|---|---|---|
| 봇이 사는 곳 | xAI 클라우드 VM (봇당 1) | 내 맥의 터미널 프로세스 | 내 Mac mini (호스트 1, 봇 N) |
| 봇의 단위 | 사람이 이름 붙여 만든 봇 | 세션 하나 | 폴더 = 봇 (후보는 자동, 활성은 폴더를 고를 때) |
| 봇의 기억 | 내부 메모리, 열람·내보내기 불가 | 세션 트랜스크립트 | 폴더 안의 파일 (CLAUDE.md·readme·todo·노트) — 열람·편집 가능 |
| 동시 세션 | 봇당 1 | 서버 모드 최대 32 | 봇당 N, 호스트 상한 |
| 관제 | 없음(봇들이 같은 VM 공유) | 없음 | 오케스트레이터 1 — PARA 전체를 보고 봇을 부린다 |
| 스케줄 | 루틴(schedule) | 없음 | 루틴(cron) — 봇별·오케스트레이터별 |
| 모델 | xAI 고정 | Claude | Claude Code · Codex (봇마다 선택, 병행 가능) |
| 접속 | 데스크톱·iOS·Android 앱 | claude.ai · Claude 앱 | 웹(PWA) 어디서나 + 얇은 macOS 셸 |
| 자체 호스팅 | 불가 | 해당 없음 | 그 자체가 자체 호스팅 |
| 가격 | $200/월 | 구독 포함 | 내 구독(Claude Max·ChatGPT) + 전기세 |
차별점 두 줄: ① 폴더가 봇이다 — 봇을 "설정"하지 않는다. 폴더의 하네스(CLAUDE.md·.claude/·readme·todo)가 곧 봇이고, 폴더를 고르는 것이 곧 시작이다. ② 오케스트레이터가 PARA 를 안다 — 폴더 생성·봇 깨우기/재우기·Inbox 분류·아카이브·주간 리뷰 같은 구조 작업을 사람이 안 한다.
3. 개념 모델
Host (Mac mini · 항상 켜짐)
├─ Orchestrator ★ 1개 — cwd = PARA 루트. 폴더를 파고, 봇을 시작·정지시키고, 부른다. 루틴을 돌린다.
├─ 봇 후보 = PARA 각 범주의 **2단계 폴더** (`2. Projects/<x>` · `3. Area/<x>` …) — 하네스가 이미 구축된 폴더
└─ Folder Bot(폴더봇) ★ 시작한 폴더당 1개 (활성)
├─ 정체성 = 폴더의 하네스 (CLAUDE.md/AGENTS.md · .claude/ · readme.md · todo.md)
├─ Session 0..N (동시) — Claude Code 또는 Codex · 각자 --resume 가능
├─ Routine 0..n (cron) — 이 봇 안에서 주기적으로 도는 세션
└─ 참조 — 볼트의 다른 폴더(Resources 등) · 연결된 코드 리포 (~/dev/*)
3.1 후보는 자동, 시작은 폴더를 고를 때 (봇 수명주기)
후보 (PARA 2단계 폴더) ──시작──▶ 활성 봇 ──정지──▶ 휴면 (기록 유지) ──Archive 이동──▶ 은퇴
▲ (사람 또는 오케스트레이터)
└── 새 폴더 만들기 + 하네스 스캐폴드 (사람이 피커에서 직접 · 또는 오케스트레이터)
어떤 폴더가 후보인지는 하드코딩하지 않는다 — 루트의 폴더 규칙(§3.5)이 정한다. PARA 프리셋일 때 규칙은 아래와 같다.
| 역할 (규칙 키) | PARA 프리셋 기본값 | 봇 |
|---|---|---|
active — 봇 후보가 되는 폴더 |
2. Projects/* · 3. Area/* |
시작하면 Project Bot / Area Bot (폴더의 종류일 뿐, 제품명은 Folder Bot) |
inbox — 정리 대기 |
1. Inbox |
봇 없음. 오케스트레이터가 규칙대로 배정 |
reference — 읽기 참조 |
4. Resources |
봇 없음. 봇들이 --add-dir 로 읽는다 |
archive — 은퇴 |
5. Archive |
봇 없음. 여기로 가면 봇은 은퇴 |
- 후보는 파생(스캔)이고 활성 목록만 저장한다 (
PARA/.folderbot/bots.yml). 봇이 많아지지 않도록 활성 상한(기본 8)을 둔다 — 넘기면 오케스트레이터가 재울 봇을 제안한다. - 시작하는 방법 넷: ① 사람이 피커에서 폴더 선택 ② 사람이 피커에서 새 폴더 만들기 → 하네스 스캐폴드 → 바로 시작 ③ 오케스트레이터에게 "X 폴더에서 시작해" ④ 오케스트레이터가 Inbox 분류·루틴 중 필요해서 제안(사람 승인).
- 폴더가 없으면 만든다 — 피커의 각 PARA 범주 첫 줄
새 폴더 만들기, 또는 오케스트레이터가 PARA 규칙에 맞는 위치·이름으로 만든다. 어느 쪽이든 하네스 스캐폴드(CLAUDE.md·readme.md·todo.md·.claude/)를 깔고 시작한다. 하네스 없는 기존 폴더를 고르면 같은 스캐폴드를 깐다. - ⛔ "깨우기" 라는 말을 UI 에 쓰지 않는다 (Dave 2026-09-12). 문구는 "에이전트와 함께 일할 폴더를 선택하세요" · 버튼 "이 폴더에서 시작" · 정지는 "정지", 상태는 "휴면".
- 폴더 안
.bot.yml(선택)로vendor: codex·repo: ~/dev/foo(코드 세션의 cwd) ·routines:·candidate: false(후보에서 제외)를 선언한다. - 볼트 밖 코드 리포는 PARA 폴더가 가리킨다(
repo:). 문서는 PARA 에, 코드는~/dev에 — Dropbox 가node_modules를 건드리지 않게.
3.2 오케스트레이터 (Orchestrator)
- 단 하나. cwd = PARA 루트. 하네스는
PARA/.claude/orchestrator.md에만 있어 프로젝트 봇이 상속하지 않는다. - 하는 일: ⓪ 폴더 규칙 관리(§3.5 — 프리셋 설치·인터뷰·개정) ① 후보·활성 봇 목록과 상태 파악 ② 폴더 생성 + 하네스 스캐폴드 ③ 봇 시작·정지·은퇴 ④ Inbox 정리 — 폴더·파일을 규칙대로 이름 짓고 배정, 해당 봇에게 위임 ⑤ 봇에게 작업 지시(세션 생성)·호출 관리 ⑥ 결과 취합·보고 ⑦ 완료 프로젝트 아카이브 제안 ⑧ 루틴 관리.
- 도구 (호스트가 MCP 로 제공):
bots_candidates·bots_list·bot_start/stop/retire·folder_create(PARA 위치·하네스 스캐폴드) ·bot_status·bot_send(세션 생성/이어가기) ·bot_sessions·routine_list/set·vault_search/tree/recent·inbox_list·folder_move/rename(되돌리기 스냅샷 동반) ·rules_get/set. - 자율 범위 (Q5): 읽기·조사·분류 제안·봇 시작/정지는 알아서, 폴더 생성·파일 이동·삭제·외부 발송은 승인 후. 되돌리기(
undo/스냅샷)가 있는 동작만 자동 허용.
3.3 세션
- 봇에게 보낸 메시지는 세션 하나로 간다. 기본은 봇의 "메인 세션"(가장 최근 활성),
새 세션은 명시적. 세션에는 이름이 있다(--name). - 동시 세션 상한: 봇당 4 · 호스트 전체 12 (메모리 기반 거절). 유휴 60분이면 워커를 내리고(절전) 다음 메시지에 같은 id 로 깨운다.
- 코드 리포 봇은 세션마다 git worktree 옵션.
- 상태 5종(단일 소스): 대기 · 실행 중 · 확인 대기(HITL) · 완료 · 오류. 모든 표면(목록·배지·푸시)이 이 하나를 본다.
3.4 루틴 (cron)
- 정의는 파일: 봇 폴더의
.bot.yml→routines: [{name, cron, prompt, vendor?, approve?}]. 오케스트레이터 루틴은PARA/.claude/routines.yml. - 실행 = 그 봇 안에 세션을 하나 띄워 프롬프트를 보내는 것. 결과는 봇의 대화에 메시지로 남고, 산출물은 폴더에 파일로 남는다.
- 예: 오케스트레이터 매일 07:00 Inbox 분류 제안 · 주간 일요일 "이번 주 각 봇 진행 요약" ·
제품_Rondo봇 매일 todo.md 미완료 리마인드. - 루틴은 승인 정책을 따로 갖는다 — 사람이 없을 때 도는 것이므로 기본은 읽기 전용 + 제안.
3.5 폴더 규칙 — 오케스트레이터의 하네스가 정한다 (Dave 2026-09-12)
사용자마다 폴더 정리 규칙이 다르다. 그래서 규칙은 코드가 아니라 루트 폴더의 하네스 파일에 있고, PARA 는 기본 탑재된 프리셋일 뿐이다.
- 정본 파일: 루트(볼트)
CLAUDE.md(Claude) 또는AGENTS.md(Codex) 의## 폴더 규칙절. 첫 실행에 PARA 프리셋으로 채워 주고, 사용자가 그 파일을 직접 고친다. 오케스트레이터는 이 절을 읽고 행동한다 — 규칙이 바뀌면 코드를 안 고쳐도 행동이 바뀐다. - 두 층으로 쓴다: ① 사람·오케스트레이터가 읽는 산문 규칙(무엇을 어디에, 이름은 어떻게, 언제 아카이브) ② 호스트가 읽는 작은 구조 블록(같은 절 안의
```yaml folder-rules펜스). 후보 스캔·Inbox 감시·은퇴 판정처럼 프로그램이 해야 하는 일은 ②만 본다.
preset: para # para | johnny-decimal | custom
roles:
inbox: ["1. Inbox"]
active: ["2. Projects/*", "3. Area/*"] # 봇 후보 = 이 글롭에 맞는 폴더
reference: ["4. Resources"]
archive: ["5. Archive"]
naming:
project: "{YYYY}-{MM}_{이름}" # 새 폴더 만들기·Inbox 정리 때 적용
harness: ["CLAUDE.md", ".claude/"] # "하네스 있음" 판정
- Inbox 는 정리 상자다: 사용자는 정리하고 싶은 폴더든 파일이든
inbox에 넣기만 한다. 오케스트레이터가 규칙을 읽어 "2026-09_예시고객로 이름 바꿔2. Projects로 옮기고 하네스를 깔까요?" 처럼 제안하고, 승인하면 옮기고 필요하면 봇을 시작한다(자율 범위는 Q5). 파일은 맞는 폴더로 배정한다. - 프리셋:
para(기본) ·johnny-decimal·custom. 첫 실행에서 고르거나, "내 방식대로 할래" 를 고르면 오케스트레이터가 인터뷰해서 규칙 절을 대신 써 준다. 폴더 구조가 없는 빈 루트면 프리셋 골격을 만들어 준다. - 규칙 변경도 대화로: "이제 Area 대신 Domains 라고 부를래" → 오케스트레이터가 규칙 절을 고치고(승인), 필요한 폴더 이동을 제안한다. 파일은 사람이 직접 고쳐도 된다 — 파일이 진실.
- ⚠ 루트 하네스는 모든 봇이 상속한다. 폴더 규칙 절은 봇들이 알아도 무해하지만(참조 폴더를 찾을 때 쓴다), 오케스트레이터 전용 지침(관제 도구 사용법·자율 범위)은
PARA/.claude/orchestrator.md에 따로 둔다.
3.6 todo.md — 하나의 파일, 두 가지 쓰임 (Dave 2026-09-12)
각 봇 폴더에 todo.md 가 있다. 관리하는 사람은 사용자다 — 잊지 않으려고 적는다. 그런데 봇도 쓴다: 사용자에게 말하려고("Tailscale 폰 설치가 아직이에요"), 그리고 자기가 다음 세션에서 하려고("변경 이력 줄은 다음에 처리"). 이 둘을 하나의 심플한 목록이 감당한다.
- 형식은 한 줄 =
- [ ] 제목: 설명. 콜론 앞이 제목(굵게), 뒤가 설명(흐리게). 설명이 없어도 된다. 완료는- [x],## 완료절로 내려간다. 중첩·우선순위·기한 문법은 두지 않는다 — 필요하면 설명에 쓴다. - 누가 적었나가 보인다. 봇이 적은 줄에는 폴더봇 표식(
<!-- bot -->한 줄 주석 또는 접미— 봇), 사람이 적은 줄에는 없다. 체크는 누구나 할 수 있다. - 봇의 규약(하네스에 주입): ① 세션 시작 시 미완료 목록을 읽는다 ② 자기가 남긴 항목은 먼저 처리하거나 왜 못 했는지 갱신한다 ③ 사용자가 할 일이 생기면 목록에 적고 대화에서 한 줄로 알린다 ④ 완료한 항목은 체크한다.
- 표면: 오른쪽 "이 폴더에서" 패널 상단에 미완료 4개까지 · 클릭하면 시트(전체 목록·추가·완료 절) · 대화 속 봇 메시지에 "할 일에 남겼어요" 카드 · 폰에서는 체크와 추가만.
- 선택한 할 일을 봇에게 맡기기 — 시트에서 항목을 고르고 보내면 그 제목·설명이 지시가 된다.
- 파일이 정본. Obsidian·에이전트·앱이 같은 파일을 본다. 호스트가 대신 쓸 때(체크·추가)도 바이트 보존 규칙을 따른다. 루틴(§3.4)의 아침 리마인드가 이 파일을 읽는다.
4. 사용자 경험 — 메신저처럼 심플하게 (Dave 2026-09-12: Grok Bot 형태)
기준은 Grok Bot 의 3분할 메신저다: 왼쪽 봇 목록 · 가운데 대화 · 오른쪽 봇의 작업 화면. 화면을 꽉 채우는 IDE 가 아니라, 동료와 채팅하는 창이다. 파일은 대화 안에서 클릭해 슬라이드로 본다.
4.0 캐릭터 — 폴더봇 (원형 아바타를 쓰지 않는다)
봇의 얼굴은 폴더다: 탭 달린 뒷판 + 얼굴이 있는 앞판. 색은 봇마다 하나(오케스트레이터는 항상 코랄), 표정은 상태 5종 — 쉼(눈 뜸) · 일하는 중(눈 내림 + 입 일자) · 확인해 주세요(입 벌림 + 앰버 느낌표) · 끝났어요(웃음) · 휴면(눈 감음 + z). 크기 3종(20 메타 · 30 헤더 · 44 목록). 목록·헤더·말풍선 옆·피커·알림 아이콘에 같은 캐릭터를 쓴다. 목업의 「폴더봇 캐릭터 시트」가 정본.
4.1 데스크톱 레이아웃 (3분할, 오른쪽은 접힘)
┌──────────────┬────────────────────────────────────────┬──────────────────┐
│ Folder Bot 🔍 │ 🗂 제품_Rondo PARA/3. Area/… ⋯ ⤴ ≫ │ 이 폴더에서 │
│ │ ┌────────────────────────────────────┐ │ ──────────────── │
│ + 폴더 선택·시작│ │ 저장된 메시지 표시 중 · 연결 복구 시 갱신 │ │ ● 세션 · PRD 작성 │
│ │ └────────────────────────────────────┘ │ 실행 중 · 12m │
│ ● 오케스트레이터│ 9월 12일 (금) 오후 2:10 │ ○ 세션 · 메인 │
│ 확인 대기 1 │ ┌────────────────────────────────┐ │ ──────────────── │
│ ● 제품_Rondo │ │ PRD v0.5 를 올렸어. │ │ 📁 파일 │
│ PRD 올렸어 │ │ 📄 docs/PRD.md 📄 todo.md │ ←칩│ ▸ docs/ │
│ ○ 강의_Found… │ │ 바뀐 건 §4 레이아웃… │ │ PRD.md ·2m │
│ 어제 │ └────────────────────────────────┘ │ ▸ 01_기획/ │
│ ○ 재무_CFO │ [승인] Bash: npm run qa [허용][항상][거부]│ todo.md │
│ (2. PROJECTS)│ │ readme.md │
│ ○ 강의_Found… │ 비활성 루틴 ⏱ 아침 todo 리마인드 │ ──────────────── │
│ │ ┌────────────────────────────────────┐ │ ⏱ 루틴 2 │
│ 🏪 마켓 │ │ + 제품_Rondo 에게 메시지… 🎤 ⏎ │ │ ⚙ 하네스 요약 │
│ 👤 대연 진 │ └────────────────────────────────────┘ │ │
└──────────────┴────────────────────────────────────────┴──────────────────┘
| 영역 | 내용 |
|---|---|
| 왼쪽 봇 목록 | 맨 위에 + 폴더 선택 · 에이전트 시작 (점선 상자, 후보 수 표시). 아래는 활성 봇 목록을 PARA 범주별 섹션(관제 · 3. Area · 2. Projects)으로 — 폴더봇 얼굴·이름·마지막 메시지 한 줄·시각·상태 점. 선택된 봇은 폴더 색 탭. 확인 대기는 섹션 안에서 맨 위. 봇이 많아질 때 (Dave 2026-09-12): ① 폭을 드래그 — 64px(접힘: 얼굴+상태 점+대기 배지) · 292px(기본) · 420px(넓게), 폭은 기억 ② 정렬 — 확인 대기 먼저 → 최근 활동(기본) · 최근 활동 · 이름 · PARA 순 ③ 묶기 — PARA 범주별(섹션 접기, 접히면 수만) · 묶지 않기 ④ 밀도 — 보통·촘촘 ⑤ 검색은 이름·경로. 활성 상한(Q3b)을 넘기면 정렬 메뉴 하단에 "휴면 N개를 은퇴시키면 가벼워져요" 한 줄. |
| 가운데 대화 | 봇과의 대화 하나. 세션은 대화 안의 스레드로 접힌다(세션 전환 탭이 아니라 "메인 · PRD 작성 · +새 세션" 드롭다운). 도구 사용은 접힌 한 줄(⏵ Edit +42 −8), 승인·질문은 카드. 루틴 실행·중지는 시스템 줄. 컴포저는 +(파일·이미지 첨부) · 마이크 · 전송. |
| 오른쪽 "이 폴더에서" (≫ 로 접힘) | ① 세션 목록과 상태 ①′ 할 일(todo.md 미완료 4개까지, §3.6) ② 폴더 파일 트리(컴팩트) — 최근 변경 순, 봇이 만든/고친 파일에 점 ③ 루틴 ④ 하네스 요약(CLAUDE.md·스킬·MCP 개수). 이 패널은 보조다 — 접어도 대화만으로 일이 된다. |
4.2 파일은 슬라이드로 본다 (IDE 가 아니다)
- 대화 속 파일 언급은 칩(
📄 docs/PRD.md)으로 렌더된다. 봇이 만들거나 고친 파일은 메시지 아래 자동으로 칩이 붙는다. - 칩·트리 항목을 클릭하면 오른쪽에서 시트가 슬라이드되어 열린다 — 앱 안 팝업. 대화는 뒤에 그대로 있고, 시트 폭은 화면의 40~60%, 드래그로 넓힘,
Esc/바깥 클릭으로 닫힘. 여러 파일은 시트 상단 탭. - 시트에서 되는 것: md 렌더(위키링크·콜아웃·프론트매터) · 이미지·PDF·코드·csv 미리보기 · md 는 그 자리에서 편집(저장 시 바이트 보존) · "봇에게 이 파일로 말하기"(칩을 컴포저에 넣음) · Finder/기본 앱으로 열기(데스크톱) · Dropbox 동기화 점.
- 전체 화면 편집이 필요하면 시트를 분리해 새 창(데스크톱)으로 띄운다. 기본은 시트다.
4.3 폰 (같은 클라이언트)
- 목록 → 대화 두 화면. "이 폴더에서" 패널은 하단 시트(위로 끌어올림), 파일 뷰어도 전체 높이 시트. 승인 카드는 대화 안 + 푸시.
- 폰에서 편집은 todo 체크·짧은 노트까지(Q8). 그 이상은 "데스크톱에서 열기" 링크.
4.4 시작 — "에이전트와 함께 일할 폴더를 선택하세요"
- 직접 고르기:
+ 폴더 선택 · 에이전트 시작→ 피커(제목이 곧 안내문). PARA 범주별로 2단계 폴더를 최근 수정순으로, 하네스 있는 폴더에 ✓ 배지, 이미 활성인 폴더는 흐리게. 각 범주 첫 줄에새 폴더 만들기— 이름을 치면 그 범주에 폴더를 만들고 하네스를 깐 뒤 바로 시작. 버튼은이 폴더에서 시작. - 오케스트레이터에게: "예시고객 폴더에서 시작해" → 후보에 있으면 즉시, 없으면 "
2. Projects/2026-09_예시고객로 만들고 하네스를 깔까요?" → 승인 → 생성·시작.
정지·은퇴는 봇 헤더 ⋯ 또는 오케스트레이터에게.
4.5 알림 — 알파 Rondo 의 알림·트레이·HUD 를 승계한다 (Dave 2026-09-12)
무엇을: 확인해 주세요(승인·질문 — 끌 수 없다, 사람 차례를 놓치면 세션이 서 있다) · 끝났어요 · 문제 있어요(세션 실패·미니 인증·연결) · 할 일 남김(봇이 사용자 항목을 적었을 때) · 루틴 결과(기본 꺼짐, 아침에 묶어서).
어디로: ① Mac 알림 센터 + 소리 — 호스트 셸(미니)과 원격 셸(맥북) 모두. 배너에 [허용][열기][거부] 가 있어 앱을 안 열고 승인할 수 있다. 클릭하면 그 대화로 점프(folderbot://) ② 메뉴바 · Dock 배지(§4.6) ③ 폰 푸시(PWA Web Push, 잠긴 화면에서 허용까지) ④ 앱 안 알림 센터(왼쪽 하단 종 → 팝오버: 모든 봇의 이벤트, 확인 대기 필터, 읽음 처리) ⑤ 메일 요약(하루 한 번, 기본 꺼짐).
언제: 조용한 시간(기본 23:00–07:00, 확인해 주세요만 통과) · 봇별 끄기(봇 헤더 ⋯).
규칙: 같은 이벤트는 한 기기에서 처리하면 모두에서 사라진다(호스트가 단일 소스). 알림은 상태 머신(§3.3)의 전이에서만 나온다 — 화면이 알림을 만들지 않는다.
4.6 메뉴바 — 폴더봇이 상태를 얼굴로 말한다 (Dave 2026-09-12: "폴더봇 디자인으로 더 센스있게")
- 아이콘 = 흰색 단색(템플릿) 폴더봇. 표정 = 모든 봇을 합친 상태: 한가함(쉼) · 일하는 중(눈 아래) · 당신 차례(입 벌림 + 배지에 확인 대기 수) · 방금 끝남(2분간 웃음) · 문제 있어요(X 눈) · 조용한 시간(눈 감음). 승인이 오면 두 번 콩콩 뛰고 멈춘다 — 깜빡이지 않는다, 배지가 남으면 충분하다.
- 팝오버: 머리에 오케스트레이터 얼굴 + 한 문장("둘이 당신 차례를 기다려요") · 봇 얼굴 줄(상태 점) · 확인 대기 카드 — 여기서 바로 허용/거부 · 지금 하는 일 · "오케스트레이터에게 한 줄" 입력 · 앱 열기 · 조용히 1시간 · 미니 상태(연결·메모리).
- 같은 팝오버가 미니(호스트 셸)와 맥북(원격 셸) 에 뜬다. 어느 맥에서 눌러도 한 번이면 끝.
- Dock 배지 = 확인 대기 수. 플로팅 HUD(알파)는 메뉴바 팝오버로 흡수 — 항상 위에 떠 있는 창은 두지 않는다(Q14).
4.7 첫 실행 (Mac mini)
호스트 설치 → 루트 폴더 선택 → 폴더 규칙 프리셋 선택(PARA 기본 · Johnny.Decimal · 내 방식대로) → 루트 CLAUDE.md 에 규칙 절 설치(이미 있으면 절만 덧붙임) → 규칙으로 스캔 → 후보 목록이 채워진다 → 오케스트레이터가 "지금 활발한 폴더 3개에서 시작할까요?" (최근 수정 기준) → Tailscale 로그인(구글) → 폰에서 주소 열기. "내 방식대로" 를 고르면 오케스트레이터가 인터뷰로 규칙을 써 준다.
4.8 비주얼
- 메신저의 단순함은 유지하되 남의 앱처럼 보이지 않게 — 원형 아바타 열·말풍선 스타일을 그대로 베끼지 않는다. 우리 것은 폴더봇 캐릭터, PARA 범주 섹션, 폴더 색 탭, 경로 브레드크럼이다. 다크 기본, 봇마다 색 하나, 상태는 점.
- 알파 Rondo 의 표면 토큰(검은 유리·팔레트·상태 언어)은 가져오되 종속되지 않는다 — 이 제품의 정본 디자인은 이 절이고, 토큰은 부품이다 (Q10).
5. 시스템 구성
[ Mac mini ] Host 앱 (로그인 항목으로 자동 실행 · 창 없음 · 트레이)
├─ Bot Registry 폴더 규칙(folder-rules)로 스캔·감시 → 후보(파생) + 활성 목록(bots.yml) · 수명주기 · Inbox 변화 감지
├─ Session Engine Claude Code: -p --stream-json 상주 워커(resume · 절전/기상 · stdio 권한)
│ Codex: codex app-server (JSON-RPC 상주 · thread/resume)
├─ Orchestrator = 특별한 봇 하나 (cwd PARA 루트 · 전용 하네스 · 관제 MCP)
├─ Scheduler cron → 세션 생성 (루틴)
├─ Vault MCP vault_* · bots_* (봇·오케스트레이터가 쓰는 도구)
├─ File API 트리·읽기/쓰기·감시 · Dropbox content_hash 판정
├─ Gateway HTTP + SSE · 기기 토큰 · 쓰기 정책 · 정적 서빙
├─ Push Web Push (VAPID) — 확인 대기·완료
└─ Auth Watchdog claude 자격증명 3갈래 판정(로그인·로그아웃·이 문맥에선 못 읽음) → 푸시
▲ tailnet HTTPS (tailscale serve) — 전송은 추상화해 두고 릴레이는 이음매만
[ 어디서나 ] Client (PWA) — 폰 3층 / 데스크톱 3열 [ Mac ] 얇은 셸 (알림·파일 대화상자·트레이)
원칙
- 파일이 진실. 봇의 기억·설정·산출물은 폴더 안 파일. 호스트가 죽어도
claude --resume이 터미널에서 된다. 호스트 전용 상태(기기 토큰·푸시 구독·세션 인덱스)만~/Library/Application Support/projectbot/. - 호스트가 유일한 주인. 볼트를 쓰고 세션을 띄우는 프로세스는 하나. 클라이언트는 파일을 직접 안 만지고 세션을 직접 안 띄운다 → 동기화 충돌이 정의상 없다.
- CLI 네이티브. 세션은 벤더 CLI 가 소유. Folder Bot 은 스폰·관찰·지휘만. 벤더가 발전하면 그대로 얻는다.
- 로컬은 원격의 특수 경우. 호스트와 클라이언트는 프로토콜 하나로만 만난다. 같은 맥에서 띄워도 경로는 같다.
- "아직 모른다" 상태를 둔다. 인증·동기화·락 판정은 참/거짓 + 제3값.
5.1 인증 벽 — 해결 방식
Mac mini 의 헤드리스 프로세스는 claude 자격증명(키체인)을 못 읽을 수 있다. 해결은 우회가 아니라 운영이다.
- 호스트는 로그인 항목으로 뜨는 GUI 앱(창 없음)이다 — 사람이 GUI 로 로그인한 세션의 자손이 되도록. 미니는 자동 로그인 + 절전 0.
- 호스트가 30분마다
claude auth status를 3갈래로 판정한다. "못 읽음" 이면 폰으로 푸시: "미니에서/login이 필요합니다." - Dave 가 Jump Desktop 으로 들어가
/login한다. 호스트는 그 뒤 자동 복귀. (Dave 확정: 이 수동 개입은 허용.) - 런북을
docs/RUNBOOK-mini.md로 둔다 — 재부팅·CLI 갱신·키체인 ACL 사고 시 절차. - ⛔ API 키(
--bare)로 우회하지 않는다 — 구독이 아니라 종량 과금이 된다.
5.2 접속과 로그인 — 결정 필요 (Q2)
| 안 | 구글 로그인 | 폰 설치물 | 서버 운영 | 남이 쓸 때 |
|---|---|---|---|---|
| A. Tailscale (추천) | Tailscale 계정 = 구글 | Tailscale 앱 | 없음 | "Tailscale 부터 까세요" 가 문턱 |
| B. Tailscale Funnel + 자체 구글 OAuth | 직접 구현 | 없음(브라우저) | 없음(공개 URL 은 생김) | 됨. OAuth·세션 관리 책임 |
| C. 자체 릴레이 + 구글 OAuth | 직접 구현 | 없음 | 있음 | 제품형. 한 달 추가 |
A 로 시작하고 전송 계층을 추상화해 B/C 를 나중에 끼운다. Dave 전용 기간엔 A 로 충분하다.
5.3 Dropbox
- 볼트 = 미니의 Dropbox 폴더. 오프라인 사용 가능 고정(온라인 전용 파일은 봇이 빈 껍데기를 읽는다).
- 동기화 판정: Dropbox 공식
content_hash(4MB 블록 SHA-256) 로컬 계산 ↔ API 메타데이터 비교 → 같음/다름/모름. 문서·트리에 점으로 표시. "Dropbox 반영됨" 을 완료 알림에 덧붙일 수 있다. (conflicted copy)감지 → 봇 배지. 고빈도 상태는 볼트에 쓰지 않는다(동기화 소음).- 경로는 realpath + NFC 정규화.
6. 데이터
| 무엇 | 어디 | 형식 |
|---|---|---|
| 봇 후보 | 파생 (PARA 2단계 폴더 스캔) — 저장 안 함 | — |
| 활성 봇 목록 | PARA/.folderbot/bots.yml |
YAML (경로·깨운 시각·벤더) |
| 봇 설정·루틴 | <폴더>/.bot.yml (선택) |
YAML |
| 폴더 규칙 | 루트 CLAUDE.md/AGENTS.md 의 ## 폴더 규칙 절 (산문 + yaml folder-rules 블록) |
md — 사용자가 직접 편집 |
| 오케스트레이터 하네스·루틴 | <루트>/.claude/orchestrator.md · <루트>/.claude/routines.yml |
md · YAML |
| 세션 트랜스크립트 | ~/.claude/projects/<cwd-slug>/*.jsonl · Codex 스레드 |
벤더 소유 |
| 봇 대화 뷰(세션 인덱스·읽음 표시) | 호스트 userData | JSON |
| 기기·푸시·토큰 | 호스트 userData | JSON (토큰은 keychain) |
| 되돌리기 스냅샷 | PARA/.folderbot/undo/ |
JSON |
7. 마일스톤
| 단계 | 내용 | 검증 |
|---|---|---|
| M0 스파이크 (1주) | ① 미니: 로그인 항목 GUI 앱이 claude 를 띄워 loggedIn:true ② Tailscale serve → 폰 브라우저 접속 ③ codex app-server 왕복 ④ Web Push 가 잠긴 iPhone 에 도착 ⑤ 폴더 규칙 블록 파싱 → 후보 목록 파생(하네스 유무 판정 포함) |
다섯 실측이 문서에 적힘. ①이 안 되면 §5.1 을 고친다 |
| M1 호스트 코어 | Bot Registry · Session Engine(Claude) · Gateway · 상태 머신 · 인증 워치독 | 픽스처 볼트 + stub CLI 로 원격 왕복 테스트 그린 |
| M2 클라이언트 | 3분할 메신저(봇 목록·대화·봇의 작업) · 파일 칩 + 슬라이드 시트 · 후보 피커 · 폰 2화면 | iPhone·PC 에서 같은 봇에 지시·승인·파일 열람 |
| M3 오케스트레이터·루틴 | 폴더 규칙 프리셋·인터뷰 · 관제 MCP(시작·정지·폴더 생성·이동·스캐폴드) · Inbox 정리 · 스케줄러 · 승인 정책 | Inbox 에 폴더를 던지면 규칙대로 이름·위치·하네스 제안이 온다 · "X 폴더에서 시작해" 한 마디로 폴더+하네스+봇 · 아침 루틴이 폰에 Inbox 제안을 보낸다 |
| M4 문서·Dropbox·Codex | 문서 뷰/편집기 이식 · content_hash · Codex app-server | 실 PARA 볼트에서 일상 작업 1주 |
| M5 셸·배포·런북 | 얇은 macOS 셸 · 호스트 패키징·자기 업데이트 · 미니 런북 | 새 미니에 30분 안에 설치 |
| V1.0 | Dave 의 일상 작업 전부가 여기서 돈다 | 2주 실사용 후 판정 |
8. 결정할 것 (Dave)
| # | 질문 | 추천 | 대안 |
|---|---|---|---|
| Q1 | 이름 | Folder Bot (2026-09-12 확정 — 가제 Project Bot 폐기). 리포 dave-jin/folderbot 로 개명 완료 |
✅ |
| Q2 | 접속·로그인 | A. Tailscale + 전송 추상화 | B · C (§5.2) |
| Q3 | PARA 프리셋의 active 기본값 |
2. Projects/* + 3. Area/* (사용자가 규칙 파일에서 바꿈) |
Resources 까지 |
| Q3b | 활성 봇 상한 | 8 (넘기면 오케스트레이터가 재울 봇 제안) | 상한 없음 |
| Q3c | "하네스 있음" 판정 | CLAUDE.md 또는 .claude/ 존재 (규칙의 harness: 로 변경 가능) |
readme.md 만으로도 후보 |
| Q3d | 규칙 파일 | 루트 CLAUDE.md 의 ## 폴더 규칙 절 (산문 + yaml 블록) |
별도 structure.yml 만 · AGENTS.md 우선 |
| Q3e | Inbox 정리 자율 범위 | 제안 → 승인 → 이동 (되돌리기 스냅샷) | 명백한 경우 자동 이동 |
| Q4 | 봇당 동시 세션 | 4 (호스트 12) | 무제한 + 메모리 거절만 |
| Q5 | 오케스트레이터 자율 범위 | 읽기·분류 제안 자동, 이동·삭제·발송은 승인 | 되돌리기 가능한 건 전부 자동 |
| Q6 | 루틴 기본 승인 정책 | 읽기 전용 + 제안 (사람 없을 때) | 봇별 "항상 허용" 규칙 |
| Q7 | 벤더 | Claude Code 기본, Codex 봇별 선택 | Claude 만 |
| Q8 | 폰 문서 편집 범위 | todo 체크·짧은 노트만 | 편집기 전체 |
| Q9 | 기획 정본 | 이 리포 docs/PRD.md |
PARA |
| Q10 | 비주얼 | Grok Bot 식 메신저 단순함이 정본, 알파 토큰은 부품 | 알파 DESIGN.md 그대로 |
| Q11 | 파일 시트 기본 폭 | 50%, 드래그 가능 | 40% · 전체 |
| Q12 | 왼쪽 목록 기본 정렬 | 확인 대기 먼저 → 최근 활동 | 최근 활동만 · PARA 순 |
| Q13 | 알림 기본 채널 | 맥 알림 센터 + 메뉴바 + 폰 푸시 켬, 메일 끔 | 폰 푸시는 확인해 주세요만 |
| Q14 | 플로팅 HUD | 메뉴바 팝오버로 흡수 (별도 창 없음) | 알파처럼 always-on-top 창 유지 |
9. 열린 질문
- Mac mini 현황: macOS 버전 · 자동 로그인 · Dropbox·Claude·Codex·Tailscale 설치 · 절전 설정.
- 가장 자주 쓸 기기 하나(iPhone?). 그 경험을 먼저 완성한다.
- 오케스트레이터가 Inbox 를 자동으로 옮겨도 되는 순간이 있나(예: 명백한 영수증→재무_CFO).
- 첫 루틴 3개를 정해 달라 — M3 의 검증 기준이 된다.
10. 구현 현황 — v0.1.0 (2026-09-12)
설치: curl -fsSL https://raw.githubusercontent.com/dave-jin/rondo-releases/main/folderbot/install.sh | bash · 런북 docs/RUNBOOK-mini.md
| 시나리오 | 상태 | 비고 |
|---|---|---|
| S1 첫 실행 | ✅ | folderbot init 이 규칙 절 설치·후보 스캔, 화면 온보딩(프리셋 3종), 페어링 코드. QR 은 아직 없음(주소 직접 입력) |
| S2 폴더에서 시작 | ✅ | 피커 · 새 폴더 만들기(naming 규칙·하네스 스캐폴드) · 오케스트레이터 bot_start/folder_create |
| S3 이동 중 승인 | ✅ | 승인 카드·AskUserQuestion 카드·SSE 실시간·Web Push(VAPID). ⚠ 푸시는 HTTPS(tailscale serve) + 홈 화면 설치 필요 |
| S4 파일 | ✅ | 칩 → 슬라이드 시트(md 렌더·이미지·PDF) · 편집(tmp+rename) · 경로 가드. Dropbox content_hash 는 미구현 |
| S5 Inbox | ✅ | 호스트가 Inbox 감시 → 패널 "정리 제안 받기" → 오케스트레이터 inbox_list/folder_move(되돌리기 스냅샷) |
| S6 루틴 | ✅ | .bot.yml cron(croner) · 승인 정책 3단(plan/acceptEdits/bypass) · 루틴 편집 시트 · 결과 푸시 |
| S7 관제 | ✅ | MCP 15종 — bots_list·bot_status·bot_send(위임 세션)·bot_sessions·상한(봇 4·호스트 12) |
| S8 끊김·복구 | ✅ | 30분 워치독 3갈래(loggedin/loggedout/unreadable) · 앰버 배너 · 푸시 · 대기열 → 복구 시 재전송 · SSE 오프라인 표시 |
| S9 정지·은퇴 | ✅ | 정지(휴면) · Archive 이동(은퇴, 되돌리기) |
| S10 할 일 | ✅ | - [ ] 제목: 설명 <!-- bot --> · 패널 4개 · 시트 · 봇에게 맡기기 · 봇 규약을 시스템 프롬프트에 주입 · todo_add MCP |
| S11 목록·알림 | ✅ | 폭 드래그(64/240–420)·정렬·묶기·밀도·알림 센터·조용한 시간 · 맥북 셸(desktop/): 메뉴바 폴더봇 아이콘 + 대기 배지, 네이티브 알림 클릭 점프, Dock 배지, 로그인 시 자동 실행, folderbot:// 딥링크. 메뉴바 팝오버(카드에서 바로 승인)는 아직 메뉴 형태 |
맥북 앱: GitHub Actions(macOS 러너)가 desktop/ 변경마다 DMG(arm64, ad-hoc 서명)를 빌드해 dave-jin/folderbot Releases desktop-v<n> 에 올린다. 앱은 호스트 주소 + 페어링으로 미니에 붙는 셸이다.
v0.1.1 (desktop-v2, 2026-09-12): 앱 안에 호스트 내장 — 미니에서도 「이 맥에서 호스트 실행」으로 터미널 없이 상주(로그인 항목 → GUI 세션이라 키체인 벽 없음) · claude setup-token 장기 토큰 모드(설정 › Claude 토큰) · 인증 실패 문구 감지 → 배너·푸시 · 파일 첨부(기기 업로드 → 첨부/, 폴더에서 고르기) · 앱 자기 업데이트(공개 프리릴리스 → 조용히 받아 두고, 미니는 세션이 전부 유휴일 때 자동 적용 · 맥북은 확인창).
v0.2.0 (2026-09-12) — V3 미니멀 UI 최종 빌드: 네 열(목록·대화·문서·패널) 44px 통일 헤더 · 문서 열(탭·미리보기/고정·폴더 안 ↑↓·자동 저장·봇 수정 배너·⌘⇧D 숨기기) · 모든 경계 드래그(열 3·섹션 2) · 좁은 창은 아이콘 열로 접힘(겹침 없음) · 실제 폴더 트리(지연 로드·정렬·필터·우클릭·이름 바꾸기·봇이 쓴 파일 비추기) · 진행 줄(펄스·활동·경과·중단) · 서브에이전트(줄→펼침→드릴인) · 생각 접힘 · 도구 4연속 접힘 · TodoWrite 위젯(멈춤) · 대기열 · 질문/권한 카드 · 기본 모델 Opus 5+높음(설정, 다음 세션부터). 목업 캔버스 「V3 정리」 페이지가 정본.
v0.3.0 (2026-09-12) — 컴포저·모바일·유리·파일 가져오기 (목업 V4·V7·V8·V9 승인분): ① 컴포저 아래 한 줄 — 모드(자동·편집 자동 수락·계획·항상 허용, 1~4키) · 모델(Fable 5.1 기본 · Opus 5 · Sonnet 5 · Haiku 4.5) · 노력(5단 슬라이더) · 컨텍스트 링(result.usage, 80%↑ 주황 + /compact) — 세션 단위, 바꾸면 워커를 내리고 같은 id 로 --resume(턴 중이면 끝난 뒤) · 슬래시 자동완성(.claude/skills|commands 봇→루트→사용자 + CLI init slash_commands) · 직전 질문 고정(헤더 아래 유리, 질문이 위로 사라졌을 때만) · ↓ 최근으로 · 답변 액션 줄(복사·다시). ② 모바일 재설계 — 탭바 없이 홈(카드 4 + 봇 목록 + 알약) → 대화(둥근 뒤로·봇 알약·폴더) → 폴더 → 문서를 밀어 넣음 · 큰 글자(17/16) · 입력 16px+maximum-scale=1(자동 줌 금지) · 100dvh · 키보드 열리면 칩 줄 숨김. ③ 새 팔레트 #141414 사다리(커서 실측)와 유리 — 떠 있는 것만(헤더·고정 질문·↓·컴포저·팝업), 스크롤 컨테이너의 형제로 두고 본문에 여백, prefers-reduced-transparency 면 불투명. ④ 파일 가져오기 — @ 언급(열린 문서 우선 퍼지 검색, 폴더째) · Finder 드래그(첨부/ 로 복사) · 트리 드래그(폴더 포함) · 우클릭 «폴더째 첨부»·«@ 로 언급» · + 메뉴 «열린 문서 첨부» · 스크린샷 붙여넣기 — 어디서 오든 같은 칩 줄, 보내는 형식(절대경로 목록)은 그대로.
v0.3.1 (2026-09-12) — 자기 업데이트 실동작: 업데이터가 이 리포(공개)의 desktop-v* 릴리스를 직접 본다(버전은 zip 이름에서, desktop/update-pick.js 순수 함수 + vitest). PAT·공개 리포 단계 제거. 화면 왼쪽 아래 버전 칩 = 확인 · 받는 중 % · 「재시작해서 적용」 · 호스트 모드 「세션 N개 끝나면 적용」(IPC fb:update-*). 받는 건 묻지 않고 적용만 묻는다 · 미니는 세션이 전부 유휴일 때 자동 적용 · 세션이 도는데 누르면 «N개 중단» 확인.
v0.3.2 (2026-09-12) — 어디서든 에이전트 (목업 V10 승인분): 폴더 선택 = PARA 트리(접기·펼치기, 활성 폴더는 펼친 채 시작, 아무 깊이든, 역할 배지 활성·참조·보관·정리 대기, 하네스/봇 있음, 펼친 폴더 안 «새 폴더 만들기», 검색은 평평하게, ↑↓ → ← ⏎, 푸터 한 줄·경로 앞 말줄임) · Resources·Archive 도 시작(보관은 확인 한 줄) · createFolder 가 루트 안 어느 폴더든 · 봇 섹션 = 1단계 폴더, 깊은 봇은 이름 아래 경로 · 트리 우클릭 «여기서 에이전트 시작 / 봇 열기 / 새 폴더 만들기 → 시작»(봇 트리는 «상위 봇과 겹쳐요» 확인) · ls 가 폴더에 harness·botId·role 을 붙임 · 메인/원격: 호스트 맥 자체 창(this-mac)은 «메인», 그 외 «원격 · 기기이름». 이름은 설정 › 이름(hostName · devices[].name), 기본값 컴퓨터 이름(scutil)·페어링 기기 종류. «Mac mini» 문구 전부 변수로 · 할 일: 제목 누르면 그 자리에서 편집(⏎ 저장 ⎋ 취소), 설명, 삭제(되돌리기 5초), 완료 보기 — todo/edit·todo/delete 는 todo.md 의 그 줄만 바꾼다(editLine·deleteLine, vitest).
v0.3.3 (2026-09-12) — 폰 홈화면 앱 마감: iOS 가 키보드 뒤 레이아웃 뷰포트를 안 돌려주는 문제는 interactive-widget 을 빼고 키보드를 visualViewport(--kb)로만 다뤄 해결. 위 여백은 상태바 블러 아래로 한 뼘 더(--sat + 26px), 아래는 홈 인디케이터에 바짝. 루트 아래가 비쳐도 줄이 안 생기게 폰의 html/body 배경을 앱 그라데이션 끝(bg2)과 같게, standalone 은 #root 를 100vh 로 고정. 업데이트 확인 30분 + 창 띄울 때, updates/log.txt. DMG 에 「검역 해제 (우클릭-열기).command」.
v0.3.4 (2026-09-12) — 레일 호버 카드 · 할 일 행(V11) · 가운데 말줄임 · 생각 표시: 펼친 목록의 봇 행에 마우스를 올리면(300ms) 오른쪽에 카드 — 이름·경로·상태·세션(모델·컨텍스트 %·절전)·할 일/루틴 수·마지막 말·시간(HoverCard, title 툴팁 대체). 할 일 행은 글이 폭을 다 쓰고 도구는 호버 때 글 위 오른쪽(폰은 항상), 긴 행은 2줄에서 «…더»/«접기» 행별 펼침(scrollHeight 측정). 폴더·파일 이름은 Finder 식 가운데 말줄임(Mid — 앞은 줄이고 꼬리 8자는 남김: 레일·트리·폴더 선택·폰 목록). 생각(thinking) 스트림을 길이 나머지로 던지던 버그(큰 청크면 한 번도 안 보임) → 150ms 시간 기준, 최종 메시지의 thinking 블록으로 빈 본문 보충, 비어 있으면 «생각 중…». DMG 의 .command 는 「처음 설치 후 읽어주세요.txt」(터미널 한 줄 복사) 로 교체.
v0.3.5 (2026-09-12) — 권한 관문 (알파 Rondo r408·r412·r423 승계): 데스크톱 첫 실행·업데이트 뒤 필수 권한이 빠지면 화면 전체 관문 — 전체 디스크 접근(호스트 맥만 필수 · 보호 폴더를 실제로 열어 보는 프로브 여러 개, ENOENT 는 «모름» 이지 «없음» 이 아님) · 알림(모든 맥 필수 · Electron 이 상태를 못 주므로 테스트 한 발 → «보였어요» 대답을 perm-ack.json 에 기억, 재서명에도 유지). 판정은 desktop/perm-core.js 순수 함수(vitest), 재기·설정 창·대답은 desktop/perms.js, 화면은 client/Perms.tsx. 창 포커스마다 다시 재서 밀어 준다(«돌아오세요» 로 끝남). 개발 실행에선 막지 않는다. 설정 › macOS 권한 › [권한 다시 확인]. 버전 칩 클릭 → 확인 결과 토스트. 스모크: 화면 검사 실패를 «건너뜀» 으로 삼키던 것을 실패로(브라우저 없음만 건너뜀).
v0.3.6 (2026-09-12) — 알림이 안 오던 원인: 번들이 서명되지 않았다: electron-builder 의 identity: null 은 «서명 생략» 이지 ad-hoc 이 아니다 — v17 까지 zip 안 번들에 _CodeSignature 가 0개였고(실측), macOS 알림 센터는 서명 없는 앱을 등록하지 않아 테스트 알림도 실 알림도 조용히 버려졌다. desktop/afterPack.js 가 dmg/zip 을 만들기 전에 codesign --force --deep --sign - 로 ad-hoc 서명하고 검증한다(알파 Rondo scripts/dmg.sh 승계). CI 는 zip 안에 _CodeSignature/CodeResources 가 있는지 확인하고 없으면 실패한다. ⚠ 서명이 생기면 cdhash 가 바뀌어 전체 디스크 접근이 풀린다 — 권한 관문이 그걸 받는다.
v0.3.7 (2026-09-13) — 폰 키보드: 루트 = 시각 뷰포트: v14 의 «standalone 100vh 고정» 이 키보드 아래에 컴포저를 묻었다(Dave 스크린샷: 입력칸이 안 보임). useKeyboard 가 visualViewport 높이·offsetTop 을 --vvh·--vvt 로 쓰고 폰 CSS 의 #root 가 그걸 높이로 쓴다 — 키보드가 뜨면 루트가 그만큼 줄어 컴포저는 늘 키보드 바로 위, 닫히면 돌아온다(하단 띠도 없음). 열림 판정은 «입력칸 포커스 + 최대 높이보다 140px 이상 축소». 스크롤 컨테이너가 줄면 맨 아래를 유지(ResizeObserver). 스모크가 가짜 visualViewport 로 키보드를 흉내 내 루트 높이·컴포저 위치·헤더 숨김·마지막 말 노출·복원을 잰다(test/tmp/phone-kb.png).
v0.3.8 (2026-09-13) — 생각 내용은 CLI 가 주지 않는다 (실측): claude -p --output-format stream-json --include-partial-messages (2.1.269) 를 Opus 5·Sonnet 5·Haiku 4.5 로 직접 돌려 보니 thinking 블록·delta 는 오지만 thinking 은 전부 빈 문자열, 서명만 있다. headless 출력은 생각 내용을 주지 않는다 — 모델 문제가 아니라 CLI 의 동작. 그래서 빈 «생각 · (내용 없음)» 행을 도구마다 남기던 것을 없앴다: 생각 항목은 본문이 생겼을 때만 채팅에 들어가고(thinkShown), 빈 채로 끝나면 조용히 버린다(endThinking). 생각 중임은 상태줄 «생각 중» 으로만 보인다. CLI 가 내용을 주기 시작하면 코드 수정 없이 다시 보인다.
v0.3.9 (2026-09-13) — 원격 맥 앱에서 이름 바꾸기·새 폴더가 안 되던 원인: window.prompt() 를 썼는데 Electron 은 prompt() 를 지원하지 않아 아무 반응 없이 끝났다. 앱 안 이름 모달(askName·AskHost, 확장자 앞까지 선택)로 교체 — 맥 앱·브라우저·폰이 같은 경험. 스모크가 트리 우클릭 › 이름 바꾸기 → 모달 → ⏎ → 트리 반영을 확인한다. (교훈: 브라우저 전용 동기 대화상자 prompt는 셸에서 금지 — confirm 도 장기적으로 같은 길.)
v0.3.10 (2026-09-13) — 폰 상·하단 여백을 elon-bookclub 방식으로: 상태바를 시스템에 맡긴다(apple-mobile-web-app-status-bar-style: default — 불투명, theme-color 색). 웹뷰가 상태바 아래에서 시작하므로 위쪽 안전영역·블러를 앱이 계산할 일이 없고, 헤더는 보통 여백(위 1012px, 높이 ≈ 64px)만 둔다. 아래는 6px 로 홈 인디케이터에 바짝(elon 의 env(safe-area-inset-bottom) + 4pb-[env(safe-area-inset-bottom)]). 핀치·더블탭·ctrl+휠 확대는 useViewportLock 으로 2중 차단(elon ViewportLock). --sat 는 식에 남겨 두어 투명 모드로 되돌려도 계산이 맞다.
v0.3.11 (2026-09-13) — 열 최소 폭 · NFD 파일명: 대화 열이 ~100px 로 눌려 글이 한 글자씩 접히던 것(Dave 스크린샷) → CHAT_MIN 360px. 저장된 열 폭(lay)은 그대로 두고 그리는 값(fit)만 창에 맞춰 줄인다 — 부족분은 문서(≥380) → 오른쪽 패널(≥200) → 왼쪽 목록(≥200) 순으로 양보. 드래그는 대화 열이 내줄 수 있는 만큼(slack)에서 멈춘다. CSS min-width 가 마지막 방어선. 오른쪽 패널의 세션·할 일 높이는 파일 트리 160px 을 남기는 선까지만. 가운데 말줄임 Mid 는 NFC 로 합친 뒤 코드포인트로 자른다 — macOS 의 NFD 파일명이 «전ㅊ ㅔ구조» 로 갈라지던 것.
v0.3.12 (2026-09-13) — 레일 순서 고정: 활동·상태로 행이 오르내리던 것을 없앴다. 섹션은 관제 → PARA 번호 오름차순, 행은 이름 내림차순(날짜 접두 폴더가 최신부터). 확인 대기·실행 중은 점·배지로만 표시.
v0.3.13 (2026-09-13) — 폰 아래 빈 띠(~100pt): iOS 26 이 키보드·입력 보조 막대를 내린 뒤 시각 뷰포트를 덜 돌려줘 루트(--vvh)가 짧게 남았다. 입력 중이 아니면 키보드가 있을 수 없으므로 그때는 시각 뷰포트 대신 최대 높이(maxH = vv·innerHeight·clientHeight 의 최댓값)를 쓴다. 포커스가 빠진 뒤 50·300·700ms 에 다시 잰다. 스모크: 입력 중 아닐 때 vv 가 60px 짧아도 루트는 전체 높이.
v0.3.14 (2026-09-13) — 폰 실행 줄 · «실행 중» 고착: ① 폰에서 «생각» 라벨이 세로로 서던 것 — CJK 는 글자마다 끊길 수 있어 flex 아이템의 min-content 가 한 글자였다 → 라벨 nowrap; flex:none. 실행 줄(생각·도구·서브에이전트·상태줄·도구 묶음)은 13px, 본문 16px 유지. 폰에선 말(실행 중·끝남·열기·중단·도구 n회) 대신 아이콘(.w 숨김 / .ic 표시). ② 호스트 재시작(자동 업데이트 포함)·워커 종료 뒤 스트리밍 답·결과 없는 도구·run 서브에이전트가 영원히 스피너로 남던 것 → core/chat.closeOpenItems(items, reason) 를 부팅 복원·worker exit·result 에서 호출(vitest). 중단 사유(«호스트가 다시 떠서 중단됨» 등)를 항목에 남긴다.
v0.3.15 (2026-09-13) — 백그라운드 서브에이전트 생애: CLI 2.1.269 를 직접 돌려 잰 순서 — Agent{run_in_background} → system/task_started{tool_use_id,is_backgrounded} → 즉시 tool_result «Async agent launched…» → result(턴 끝) → 부모 id 단 줄들 → system/task_notification{tool_use_id,status,summary} → CLI 가 스스로 새 턴(init→답→result). 종전엔 즉시 tool_result 로 «끝남» 을 찍어 돌고 있는데 끝난 듯 보였다. 이제 bg 표시 → 턴이 끝나도 «실행 중», 알림이 오면 끝남+요약. 세션 정보 bg(도는 수) — 유휴 회수와 데스크톱 업데이트 적용은 bg=0 일 때만. 스텁이 같은 순서를 흉내 내고 스모크가 launch→running→notification→done→후속 턴을 검사한다. 호스트가 재시작되면(자동 업데이트 포함) 그 에이전트는 사라지므로 «호스트가 다시 떠서 중단됨» 으로 마감된다 — 이게 Dave 스크린샷의 원인(업데이트 재시작 뒤 «실행 중 · 도구 42회» 고착).
v0.3.16 (2026-09-13) — 진행 애니메이션 1번 · 표정 C · 자잘한 3건: (Dave 선택) 상태줄의 점을 8px 로 키우고 물결(ripple), 활동 문구는 시머. 폴더봇은 눈·입·표식 그룹에 CSS 키프레임(깜빡임 4.2s · 시선+타이핑 · 눈썹+! · z · 미소 2회 · 흔들림) + 모서리 상태 배지(일하는 중 숨쉬기 · 확인 튕김). 레일·스트립·폰 목록의 별도 점은 배지가 대신. prefers-reduced-motion 이면 전부 정지, ≤20px 는 입 애니메이션 생략. 자잘한 것: 신호등을 헤더 44px 중앙에(trafficLightPosition), 업데이트 칩 문구 축약+말줄임, 데스크톱 대화 헤더 불투명 + 아래 28px 페이드 띠(글이 헤더에 걸치지 않음).
v0.4.0 (2026-09-13) — 할 일 2.0 (목업 V15 승인분): 패널이 todo.md 의 거울이 됐다. ① 문서의 ## 제목(절)이 그대로 섹션 — parseTodo 가 각 줄에 section 을 붙인다(맨 위 «# todo» 는 문서 제목이라 절이 아니다). «완료» 절은 접힌 채 시작. ② 목록은 제목 한 줄만, 설명은 더블클릭(폰은 탭)으로 그 행을 펼칠 때만 — 설명이 있는 행에만 › 표식. ③ 편집은 펼친 자리에서 한 칸에 «제목: 설명»(첫 «:» 앞이 제목 — 파일 형식 그대로). ④ 체크하면 «완료» 절 끝으로 내려간다(toggleAndMove), 풀면 제자리. ⑤ 끌어서 순서·절 이동(moveLine + POST todo/move) — 그 줄만 옮기고 절 제목·빈 줄은 보존. ⑥ 새 할 일은 각 절 끝에 인라인(addLine(…, section)). ⑦ 헤더의 「todo.md」 로 원문을 열어 자유롭게 고칠 수 있다. 🔴 교훈 둘: 한글에는 정규식 \b 가 성립하지 않아 «완료» 절 판정이 영원히 거짓이었다(스모크로 잡음) · 새 버튼에 .doc 클래스를 붙였다가 «문서 열이 열렸나» 검사가 그 버튼을 잡았다 — 화면 검사가 쓰는 클래스 이름은 고유해야 한다.
v0.4.1 (2026-09-13) — 라이트 테마 · 폰 상단 경계선: ① 폰에서 상태바 아래(루트 배경 bg2)와 헤더(bg)의 색이 갈려 가로선처럼 보였다 → 폰 배경을 한 색(--bg)으로 통일하고 .app 의 세로 그라디언트를 없앴다. ② 라이트 테마 — 색은 전부 토큰이라 :root[data-theme="light"] 한 벌로 갈린다. 하드코딩돼 있던 페이드·단색 유리·그림자도 토큰으로 뽑았다(--fade0/--fadeA/--fadeB/--solid/--hover0/--shadow/--onAccent). 「시스템·라이트·다크」는 설정 › 화면에서 고르고 fb:theme 에 저장한다. «시스템» 은 JS 가 matchMedia 로 지금 값을 정해 data-theme 에 넣는다(같은 팔레트를 CSS 에 두 벌 두지 않으려고) — 시스템 설정이 바뀌면 즉시 따라간다. theme-color(상태바 색)도 함께 바뀐다. ⚠ 헤드리스 크로뮴은 prefers-color-scheme: light 라 QA 화면은 fb:theme=dark 로 고정해 찍는다.
v0.4.2 (2026-09-13) — 폰 «쓸어서 처리» (목업 V16 승인분): 폰 할 일 행에 붙어 있던 도구 아이콘(연필·맡기기·삭제)을 없애고 좌·우 × 짧게·길게 네 동작으로 바꿨다. 기본값은 → 짧게 편집 · → 길게 완료 · ← 짧게 메뉴(시트) · ← 길게 삭제. 임계는 행 너비의 25%(짧게)·45%(길게)이고, 끄는 동안 뒤 배경이 그 동작 색으로 차고 아이콘이 따라오며 길게 구간에 들어가면 원이 채워지고 짧게 진동한다 — 손을 떼는 순간의 예고가 실행된다. 판정은 client/swipe.ts 의 순수 함수(slotOf·actOf, vitest 4건)에만 있고 화면은 결과만 그린다. 설정 › 할 일에서 네 자리에 편집·완료·메뉴·삭제·맡기기·펼치기·없음 중 무엇을 놓을지 고르고 진동을 끌 수 있다(fb:swipe). 세로 움직임이 먼저면 스와이프로 보지 않아 목록 스크롤이 걸리지 않는다. 데스크톱은 그대로 호버 도구. ⚠ setPointerCapture 는 합성 이벤트에서 던질 수 있어 try/catch — 제스처가 거기서 멈추면 안 된다.
v0.4.3 (2026-09-13) — 입력창이 화면 밖으로 나가던 것: v0.3.13 이 «키보드 뒤 시각 뷰포트를 덜 돌려주는» 문제를 풀려고 본 적 있는 최대 높이를 기억해 루트 높이로 썼는데, 한 번이라도 크게 잡히면(주소창 변화·회전·부분 스크롤) 루트가 화면보다 커져 컴포저가 아래로 밀려났다(Dave 스크린샷). 최대값은 지어낸 숫자다 — 닫힌 상태의 정답은 브라우저가 아는 100dvh 이고, 열린 상태의 정답만 시각 뷰포트다. 이제 --vvh 는 키보드가 열렸을 때만 설정하고 닫히면 지운다(→ 100dvh 로 복귀). 그리고 #root { max-height:100dvh } 가 마지막 방어선이라 어떤 계산이 틀려도 입력창이 화면을 넘지 않는다. 스모크가 시각 뷰포트를 실제보다 크게 보고하는 상황까지 흉내 내 컴포저가 화면 안에 남는지 검사한다.
v0.4.4 (2026-09-13) — 폴더 찾기가 «한글을 아예 못 찾던» 것: Dave 보고 — "폴더 이름의 이니셜부터만 검색이 되네요 … '트레바리' 같은 경우에는 아예 검색이 안 돼요." 원인은 둘이고 둘 다 검색을 통째로 죽인다. ① NFD: 맥은 파일 이름을 자모가 분리된 NFD(ㅌ+ㅡ+…)로 저장하는데 입력기가 만드는 질의는 NFC(트)다 — 코드포인트가 달라 includes 가 무조건 실패한다. 그래서 날짜·영문 같은 ASCII 조각만 걸렸고 그게 «앞머리만 된다» 로 보였다. ② 색인이 끊겨 있었다: 피커가 files?depth=4 를 색인으로 썼는데 그 함수(tree)는 파일까지 세며 400개에서 끊는다 — 볼트 앞쪽 400개만 색인되어 뒤쪽·깊은 폴더는 애초에 목록에 없었다. 처방: 판정을 core/search.ts 한 곳으로 모으고(비교 전 양쪽 NFC, 가운데 일치 60점 · 앞머리 80 · 전체 100 · 경로 40 · 초성 30(ㅌㄹㅂㄹ→트레바리) · 띄엄띄엄 10), 색인은 폴더만 담는 allDirs(깊이 6 · 상한 4000)로 바꿨다. 걸린 자리는 브랜드 코랄로 굵게 칠하고 결과 행은 «이름 먼저, 경로는 아랫줄 흐리게» 로 바꿨다. 같은 NFD 버그가 있던 @ 파일 붙이기 · 문서 열 찾기 · 컨텍스트 패널 거르기도 같은 함수로 옮겼다. 검사: vitest 10건(NFD 재현 포함 — 옛 색인이 실제로 못 닿았음을 단언한다) + 스모크가 픽스처 볼트에 NFD 이름의 깊은 폴더를 두고 «트레바리»·«ㅌㄹㅂㄹ» 로 찾아 강조 색까지 잰다. ⚠ 정규화는 비교에만 쓴다 — 파일에 손대는 경로는 원문 그대로여야 ENOENT 가 안 난다(host/files.ts 의 guard() 머리말).
v0.4.5 (2026-09-13) — 키보드가 «다시» 올라올 때 타이핑 자리가 안 잡히던 것: Dave 스크린샷 — 키보드 위에 컴포저 대신 iOS 의 입력 보조 막대와 잘린 대화가 있다. 원인은 열림 판정식 하나다. 종전에는 키보드가 먹은 높이를 innerHeight − vv.height − vv.offsetTop 으로 쟀는데, iOS 는 포커스된 칸을 보이게 하려고 시각 뷰포트를 아래로 민다 — 그때 offsetTop 이 커지면서 이 값이 문턱(140) 아래로 떨어지고, 앱이 «키보드가 닫혔다» 고 판정해 --vvh 를 지웠다. 루트가 100dvh 로 되돌아가니 컴포저가 키보드 밑에 묻힌다. 키보드가 먹은 높이는 레이아웃 뷰포트와 시각 뷰포트의 차이 하나이고, 밀린 만큼은 이미 --vvt 가 갚고 있다 — 그래서 offsetTop 을 빼는 항을 없앴다. 함께 ① 뷰포트 메타에 interactive-widget=resizes-content 를 넣어 지원하는 판에서는 브라우저가 레이아웃 뷰포트를 줄이게 했고(그러면 100dvh 가 곧 정답이라 JS 가 할 일이 없다), ② 입력칸에 포커스가 들어오면 대화를 맨 아래로 붙인다 — 읽으려고 위로 올려 둔 채 답을 쓰면 무엇에 답하는지가 안 보였다. 스모크가 visualViewport.offsetTop 을 300px 밀어 놓고 루트·컴포저가 보이는 띠 안에 남는지 재고(옛 식으로 되돌리면 실제로 빨개진다 — 확인함), 포커스 뒤 대화가 바닥에 붙는지도 잰다.
v0.4.6 (2026-09-13) — 레일 폴더봇 크기 · 마우스 오버 확대: Dave — "좌측 폴더 아이콘이 크게 보기 작게 보기 옵션이 있으면 좋겠어. 현재는 너무 작게 보여서 귀여운 폴더 표정이 잘 안 보여." + "마우스 오버했을 때는 크게 보이면 더 좋아." 표정은 이 제품의 상태 표시인데 16px 에서는 눈·입이 안 보인다. 설정 › 화면에 작게(16) · 보통(24) · 크게(32) 를 두고 기본값을 보통으로 올렸다(fb:icon, 이 기기에만 남는다). 고른 값은 CSS 변수 --fbi 로도 나가 줄 높이가 아이콘을 따라간다. 마우스를 올리면 transform:scale(1.45) 로 한 번 더 커진다 — transform 이라 줄이 밀리지 않는다(레이아웃은 그대로, 그림만). 호버 상세 카드의 폴더봇도 18 → 28px. 움직임을 줄이는 설정에서는 전환 없이 1.2배만. 스모크가 «크게» 를 눌러 아이콘·줄 높이가 함께 커지는지, 호버에 1.2배 이상 커지는지 잰다.
v0.4.7 (2026-09-13) — 세션 삭제: Dave — "세션에 세션 삭제 기능이 없네." 호스트에는 DELETE /sessions/:id 가 이미 있었고(워커를 내리고 기록을 지운다) 화면에만 길이 없었다. 세션 행에 ✕ 를 달았다 — 데스크톱은 마우스를 올렸을 때(또는 선택된 행에), 폰은 호버가 없으니 늘 보인다. 되돌릴 수 없으니 한 번 묻고, 돌고 있는 턴이 있으면 "지금 돌고 있는 턴이 중단돼요" 를 함께 말한다. 지운 게 보고 있던 세션이면 남은 첫 세션으로 옮긴다. 🔴 붙이는 김에 되살아나던 버그를 잡았다 — remove() 는 파일에 «지웠다» 표식을 남기는데 부팅 때 그 표식을 아무도 안 읽어서, 호스트를 다시 띄우면 지운 세션이 목록에 그대로 돌아왔다(스모크가 실제로 재현했다). 이제 생성자가 deleted 를 건너뛴다. 검사: 스모크가 세션을 만들어 지우고 → 목록·기록에서 사라지는지 → 호스트를 죽였다 다시 띄워도 안 돌아오는지 재고, 화면에서는 «취소하면 안 지워지고 확인하면 지워지며 보던 세션은 남는지» 를 잰다.
v0.4.8 (2026-09-13) — 키보드 3차: 문턱을 버렸다 · interactive-widget 되돌림: Dave — "여전히 모바일 채팅 영역이 바로 키보드 위로 올라오지 않아." 이번 원인은 한 라운드 전에 내가 넣은 뷰포트 메타다. interactive-widget=resizes-content 를 넣으면 iOS 26 이 레이아웃 뷰포트까지 줄이는데, 그러면 innerHeight − vv.height 가 0 이 되어 «키보드가 140px 이상 먹었나» 라는 문턱이 «닫혔다» 로 떨어진다. 그 길로 가면 루트가 100dvh 로 돌아가는데 그 dvh 는 줄어든 만큼을 반영하지 않아 루트가 화면보다 커지고 대화·입력창이 키보드 위로 안 올라온다. 교훈은 두 가지다. ① 키보드 높이를 정하는 장치는 하나여야 한다 — 메타와 JS 가 서로 다른 답을 내는 순간 이 버그가 난다. 메타는 되돌렸고, index.html 에 «다시 넣지 마라» 를 남겼다. ② 문턱으로 «열렸나» 를 판정하지 않는다 — 세 라운드 연속 이 문턱이 틀렸다(최대 높이 기억 → offsetTop 빼기 → 레이아웃 축소). 정답은 «입력 중이냐» 하나다. 입력 중이면 보이는 영역이 곧 시각 뷰포트이고, 키보드가 닫혀 있었다면 vv.height 가 화면 높이와 같아 100dvh 와 같은 값이 되니 해가 없다. 문턱은 헤더 숨김 같은 화장에만 남겼다. 스모크에 «레이아웃 뷰포트까지 함께 줄어드는 판»(__kbBoth)을 추가했다 — 옛 문턱으로 되돌리면 루트 844 · 보이는 영역 508 로 실제로 빨개진다(확인함).
v0.5.0 (2026-09-13) — 폰 할 일 다시 짓기 · 폰 폴더 고르기 · 사용량 · 에이전트 제공자: Dave 의 세 가지 불만(«끌어서 순서 변경이 안 돼 · 디자인이 예쁘지가 않아 · 우측 여백이 너무 많아»)과 이어진 요구를 한 라운드에 처리했다. ① 폰 할 일 — 동그란 체크 22px, 제목이 남은 폭을 전부 쓰고 두 줄까지, ↳ 설명 한 줄, 오른쪽 꺾쇠 제거(그 자리가 여백의 원인이었다), 태그·칩 없음(Dave), 줄 높이 5262px. 탭하면 펼쳐져 상세와 할 일거리가 나오고, 0.35초 길게 눌러 끌어 옮긴다. 편집은 아래에서 올라오는 시트 하나(제목·상세·절·삭제)이고 «새 할 일» 도 같은 시트다. 쓸기·집기·스크롤 판정은 /.folderbot/usage.jsonlclient/gesture.ts 순수 함수 한 곳(유닛 5건). ② 폰 폴더 고르기(V17 B안) — 한 단계씩 들어간다. 이름이 폭을 전부 쓰고 상태는 아랫줄, 시각 표시를 빼고, 푸터는 두 줄(경로 + 48px 버튼), 취소는 헤더 ✕. ③ 사용량 — 실측 결과 요금제 한도는 로컬에 없어 «내 예산 − 쓴 양» 으로 그린다. Stop 훅이 쌓는 `과 기록 직접 훑기를 합쳐GET /api/usage하나로 내주고, 메뉴 막대·앱·폰이 그 하나를 읽는다. **뺄셈은 호스트에서 한 번만**. 기록이 없는 도구는 줄 자체를 안 그린다(Dave). ④ **에이전트 제공자** — 실행 파일이 실제로 있는 CLI 만GET /api/agents로 내준다. 🔴 이 라운드가 잡은 **조용한 버그 셋**: 같은 이벤트를 두 핸들러가 받아 **쓸기가 통째로 죽은 것**(JSX 스프레드 순서),position:fixed 인 시트가 **키보드 밑에 깔린 것**(--kbh신설), 그리고 **클래스 이름.sub` 겹침**으로 ↳ 와 설명이 두 줄로 갈린 것 — 셋 다 스모크가 잡았고 각각 회귀 검사를 남겼다.
미구현·다음: Codex(codex app-server) · 메뉴바 팝오버(카드에서 바로 승인) · Dropbox 동기화 점 · QR 페어링 · Johnny.Decimal 실제 스캔 규칙 · 규칙 인터뷰(custom) · 개발자 서명·공증.
11. 사실 확인 (2026-09-12)
- Grok Bot — xAI, 2026-08 베타. 봇당 영속 클라우드 VM(브라우저·파일·터미널·커넥터·MCP). 데스크톱·iOS·Android. 루틴(스케줄)·다중 봇 협업. 승인: 한 번 허용/항상/거부 + Auto Review 규칙. 메모리 열람·내보내기 불가, 자체 호스팅 불가. $200/월 또는 SuperGrok Heavy·Cursor Ultra 포함.
- Claude Code Remote Control — GA 2026-08.
claude remote-control서버 모드(동시 32, 4시간 내 복귀). 로컬 프로세스 종료 시 오프라인. - Codex — Codex Remote(ChatGPT 앱, GA 2026-06, 호스트 로그인 상태 필요, API 없음) ·
codex app-serverJSON-RPC(stdio/WebSocket,thread/resume). - Claude Agent SDK — 세션 파일
~/.claude/projects/<encoded-cwd>/*.jsonl,resume/fork,listSessions(). - Tailscale Serve — tailnet HTTPS, identity 헤더, 구글 로그인, iOS/Android 앱.
- Dropbox — 동기화 상태 공식 API 없음.
content_hash알고리즘 공개. - Mac mini 헤드리스 인증 — SSH·launchd·osascript 에서
claude가 키체인을 못 읽는 실측 있음(2026-09-06). 사람이 GUI 로 띄운 셸 +/login은 됨.
부록 A. Rondo 알파에서 가져올 부품 (이력 아님 — 부품 목록)
| 부품 | 알파 위치 | 규모 | 용도 |
|---|---|---|---|
| 게이트웨이 | main/RemoteGateway.ts |
685줄 | tailnet 바인드·토큰·SSE·페어링·정적 서빙 → Gateway |
| Tailscale 연동 | main/Tailnet.ts |
98줄 | 상태·up |
| 세션 워커 | main/ClaudeSession.ts · 절전/기상 · core/sessionOwnership.ts · core/idleReclaim.ts · main/claudeProjects.ts(NFC/NFD) |
L | Session Engine |
| Codex 어댑터 | main/CodexSession.ts |
272줄 | 이벤트 변환 참고 (app-server 로 교체) |
| 원격 계약 | core/remoteContract.ts |
243줄 | 프레임·쓰기 정책의 출발점 |
| 채팅 동기화 | core/chatSync.ts · composerSync.ts |
S | 두 화면 동시 열람 |
| 문서 편집기 | renderer CM6 라이브 프리뷰 + churn 0 하네스 | L | 데스크톱 문서 편집 |
| 폰 3층 레이아웃 | renderer CSS (JS 0) | S | 폰 화면 |
| 디자인 시스템 | DESIGN.md |
— | 표면·팔레트·상태 언어 |
| 볼트 MCP·하네스 스캔 | main 내장 MCP · .claude 정적 스캔 |
M | Vault MCP · 봇 정체성 표시 |
| 알림·트레이·HUD | main | M | macOS 셸 |
| QA 관문 | scripts/qa.mjs · 픽스처 볼트 · stub CLI · 화면 검사 |
L | 그대로 |
| 배포 | publish-update.sh · 자기 업데이트 · 테스트 갈래 |
M | 호스트 갈래 추가 |
변경 이력
| 날짜 | 버전 | 내용 |
|---|---|---|
| 2026-09-12 | v1.0 | 구현 시작 — v0.1.0 테스트 빌드 · §10 구현 현황 신설 · v0.1.1 호스트 내장 앱·토큰·첨부 · v0.2.0 V3 미니멀 UI |
| 2026-09-12 | v0.9 | Dave 지시 — 왼쪽 목록 폭 조절·정렬·묶기·밀도(§4.1) · §4.5 알림(알파 승계: 알림 센터·배너 승인·폰 푸시·앱 안 알림 센터·조용한 시간) · §4.6 메뉴바(폴더봇 표정 아이콘·팝오버에서 바로 승인·HUD 흡수) · Q12~Q14 · 목업 S11 |
| 2026-09-12 | v0.8 | Dave 지시 — §3.6 todo.md 신설: 사람과 봇이 같이 쓰는 심플 목록, 한 줄 = 제목: 설명, 봇 표식, 봇 규약 4가지, 표면(패널·시트·카드) · 목업 S10 추가 |
| 2026-09-12 | v0.7 | Dave 지시 — §3.5 폴더 규칙 신설: 규칙은 루트 CLAUDE.md/AGENTS.md 의 절(산문 + yaml 블록), PARA 는 프리셋, 사용자가 수정·인터뷰로 커스텀 · Inbox = 정리 상자(폴더·파일을 넣으면 오케스트레이터가 규칙대로 배정 제안) · 후보 판정을 규칙 기반으로 · Q3d·Q3e 추가 |
| 2026-09-12 | v0.6 | Dave 피드백 — 폴더봇 캐릭터(§4.0) · "깨우기" 폐기 → "에이전트와 함께 일할 폴더를 선택하세요"/"이 폴더에서 시작"/"정지" · 피커에서 새 폴더 만들기 로 직접 생성·시작 · 왼쪽 목록을 PARA 범주 섹션으로 · 목업 v2 링크 |
| 2026-09-12 | v0.5 | §4 전면 교체 — Grok Bot 식 3분할 메신저 · 파일은 칩 + 슬라이드 시트(IDE 아님) · 봇 깨우기 두 갈래(PARA 트리 피커 / 오케스트레이터) · Q10·Q11 추가 |
| 2026-09-12 | v0.4 | Dave 정정 반영 — 봇은 자동 생성이 아니라 필요할 때 깨운다(후보=PARA 2단계 폴더·활성 상한) · 오케스트레이터가 폴더 생성·하네스 스캐폴드·깨우기/재우기·호출 관리를 맡음 · Q3b·Q3c 추가 |
| 2026-09-12 | v0.3 | 새 프로덕트로 다시 씀 — Project Bot · 폴더=봇 · 오케스트레이터 · 루틴 · Grok Bot 대조 · 인증 벽은 운영(Jump Desktop /login)으로 해결 · 알파 이력 제거, 부품 목록만 부록으로 |
| 2026-09-12 | v0.2 | (폐기) 알파 연장선 관점의 초안 |
| 2026-09-12 | v0.1 | (폐기) 첫 초안 |
v0.5.1 (2026-09-13) — 에이전트 고르기(V24) · 설정 다시 짓기(V25) · 맥 사용량은 메뉴바로: 승인된 두 목업을 구현했다. ① 에이전트 고르기 — 폴더를 시작할 때 «누구와 일할까요» 를 먼저 묻고, 그 아래에 어떤 환경으로 뜨는지(폴더 · 지침 · 모델 · 권한)를 함께 보여 준다. 회사 표식으로 가르고, ⛔ 안 깔린 에이전트는 칸 자체가 안 나오며(호스트가 실행 파일을 확인한다) 하나뿐이면 화면을 아예 건너뛴다 — 고를 게 없는데 묻는 건 문턱만 하나 더 만드는 것이다. 고른 값은 봇의 vendor 로 남고 «시작할 때 고른 것» 이 .bot.yml 보다 이긴다. ② 설정 — 한 장에 여덟 덩어리가 쌓여 있던 것을 왼쪽 목차 아홉 칸(일반 · 호스트·연결 · 에이전트 · 하네스 · 사용량 · 화면 · 할 일 · 알림 · 권한·보안)으로 갈랐다. 가르는 기준은 «언제 만지나» 이고, 줄은 늘 제목 · 설명 · 조작 세 칸이라 설명이 길어져도 조작 자리가 안 흔들린다(스모크가 잰다). 검색은 제목과 설명을 함께 찾고, 되돌리기 어려운 것은 칸의 맨 아래에 빨간 글씨로. 폰에서는 목차가 목록이 되고 누르면 그 칸만 한 화면으로 들어간다. ③ 커넥터 · 스킬 · 하네스 — Dave 의 질문(«폴더별 하네스는 어떻게 보여져야 할지 고민») 에 두 층으로 답했다. 모두가 함께 쓰는 것은 설정 › 에이전트(범위 칩: 사용자 · 볼트 · 폴더 · 내장), 폴더마다 다른 것은 그 폴더 패널의 «지침 · 하네스» 칸. 설정의 하네스 칸은 한눈에 훑는 보기 전용 표다 — 하네스는 폴더에 딸린 것이라 설정에 목록으로만 두면 «지금 어느 폴더 이야기인가» 가 사라진다. 🔴 «붙어 있다» 는 설정에 있다는 뜻뿐이라 실제 연결을 확인하기 전에는 회색 점으로 둔다. ④ 맥의 사용량은 메뉴바에서만(Dave: "맥에서는 그냥 메뉴바 안에서만 이게 보이면 좋겠어") — 앱 상태바의 칩과 팝오버를 걷었다. 폰은 첫 화면 맨 위 한 줄 띠 하나이고 누르면 카드가 시트로 올라온다. 두 표면에 다 두면 같은 숫자가 두 번 보이고 왼쪽 아래가 또 비좁아진다. ⚠ 이 라운드에도 클래스 이름 겹침이 하나 있었다 — 새 설정 줄을 .srow 로 지으려다 그게 이미 패널의 세션 줄이라 .setr 로 바꿨다(r508 의 .sub 와 같은 사고를 쓰기 전에 잡은 것).
v0.5.2 (2026-09-13) — 시작한 뒤에도 누구인지 보인다 · Codex 로 진짜 띄운다: V24 목업의 마지막 칸(«시작한 뒤 — 누구인지 늘 보인다»)과 Codex 연동을 붙였다. ① 회사 표식 — 레일 · 폰 목록 · 대화 머리 · 호버 카드의 폴더봇 오른쪽 아래에 Claude/Codex 마크가 붙는다. ⛔ 깔린 제공자가 하나면 배지를 아예 안 그린다 — 모든 봇에 같은 배지가 붙으면 아무것도 안 알려 준다(«Codex가 없으면 아예 안보여야 해» 와 같은 원칙). ② 같은 폴더에 형제로 — 폴더 하나에 에이전트 하나가 원칙이지만 Claude 와 Codex 는 읽는 지침(CLAUDE.md · AGENTS.md)이 달라 나란히 둘 수 있다. 그래서 «이미 있나» 판정을 rel 에서 rel + vendor 로 바꿨고, 형제가 있을 때만 이름 뒤에 «· Codex» 가 붙는다. ③ Codex 워커 — codex exec --json 의 줄을 host/codex.ts 에서 Claude 의 stream-json 모양으로 옮긴다. 옮기는 층이 하나라 화면·저장·알림은 어느 CLI 인지 모른다. Claude 와 다른 두 가지를 계약으로 적어 뒀다: 턴마다 새 프로세스(codex exec 는 상주하지 않는다 — 두 번째 턴부터 resume <세션 id>)이고, stdio 승인 프로토콜이 없다(우리가 못 묻는 것이라 기본을 --sandbox read-only 로 둔다). 🔴 모르는 줄은 버리지 않는다 — Codex 의 이벤트 이름은 판마다 바뀌므로 표에 없는 줄은 활동 줄로 흘려보내고 첫 줄을 lastError 에 남긴다. ⚠ 남은 확인 하나: 이벤트 이름은 문서 기준이고 이 컨테이너에 Codex 가 없어 가짜 CLI(test/fixtures/stub-codex.mjs)로만 검증했다 — 옮기는 층·세션 이어가기·도구 카드는 초록이지만, 진짜 이름이 다르면 «모르는 줄» 로 떨어진다. Dave 의 맥에서 한 턴 돌려 보고 이름을 맞춘다.
v0.5.3 (2026-09-13) — 알림 버튼 줄은 바닥에 · 표식은 배경 없이 폴더 옆에: Dave 의 두 지적을 고쳤다. ① 폰 알림 — 목록이 화면 중간에서 끊기고 그 아래가 통째로 비어 있었다. 원인은 NotifyCenter 가 본문에 인라인으로 걸어 둔 maxHeight: 55vh 다. 폰 미디어쿼리의 .modal-b { flex:1 } 을 그 상한이 이겨, 모달은 화면 높이인데 내용만 중간에서 멈췄다. 🔴 높이를 컴포넌트 안에서 인라인으로 묶지 않는다 — 상한은 데스크톱 전용 CSS 규칙(@media (min-width:761px) .nmodal .modal-b)으로 옮기고, 폰은 본문이 아래까지 차고 버튼 줄이 안전 영역 바로 위에 붙는다. 세 버튼이 들쭉날쭉 감기던 것도 두 칸 격자로 눕혀 높이를 맞췄다. 스모크가 «버튼 줄 아래 빈 곳 ≤ 24px · 본문이 모달의 절반 이상» 을 잰다. ② 회사 표식 — 배지 뒤에 깔던 검은 원판(background: var(--bg) + 링)이 목록마다 얼룩처럼 보였다(Dave: "배경 투명으로 폴더 옆에 붙어야 해"). 원판을 걷고 표식만 폴더봇 오른쪽 아래에 붙였다. ③ 그러면서 이름이 잘리는 것을 둘 잡았다 — Mid(가운데 말줄임)의 앞뒤 조각이 각각 inline-flex 아이템이라 경계의 공백이 접혀 «제품_Rondo· Codex» 로 붙어 있었고(white-space:pre 로 조각 안 공백을 지킨다), 형제 이름의 «· Claude» 는 떼었다 — 좁은 레일에서 정작 폴더 이름이 «제품_R…» 로 잘렸다. 어느 쪽인지는 표식이 이미 말해 주므로 글자는 기본이 아닌 쪽(Codex)에만 쓴다(V24 목업이 그렸던 모양 그대로다 — 내가 양쪽에 붙였던 게 틀렸다).
v0.5.4 (2026-09-13) — 다시 붙으면 «본 것» 을 전부 다시 읽는다 (폰만 낡던 버그): Dave — "왜 수정된 파일이 원격 모바일(맥 원격은 됨)에서 바로 적용이 안 돼? 할 일도 적용이 안 되어 있네." 🔴 원인은 둘이고 둘 다 폰에서만 난다. ① 놓친 프레임을 다시 안 읽었다 — 화면이 사는 길은 SSE 한 줄뿐인데, 맥은 그 줄이 안 끊겨 todo·chat·files 프레임을 계속 받는다. 폰은 잠그거나 다른 앱에 갔다 오는 사이 줄이 끊기고 그동안 온 프레임은 영영 없다. 다시 붙을 때 하던 일은 /api/state 새로 읽기 하나뿐인데 그 응답에는 할 일도 대화도 문서도 없다 — 그래서 봇 목록·알림만 최신이고 할 일 패널과 열린 문서는 옛날 것 그대로였다. 이제 다시 붙으면 화면이 들고 있던 것(할 일 · 열어 둔 대화)을 이름으로 하나씩 다시 읽고 파일을 읽는 화면(트리·문서 탭)에는 filesTick 을 한꺼번에 올린다. ⛔ «다시 붙었다» 를 «상태 새로고침» 과 같은 뜻으로 쓰지 않는다 — 끊긴 동안 놓친 것은 프레임이지 상태가 아니다. ② 조용히 죽은 줄을 몰랐다 — iOS 는 백그라운드에서 돌아오면 이 스트림이 오류도 종료도 없이 멈춘다. 읽기는 영원히 기다리고 화면은 붙어 있다고 믿는다. 그래서 호스트가 8초마다 보내는 박동(: hb)을 마지막으로 받은 시각으로 재고, 30초 넘게 아무것도 안 오면 끊고 다시 붙는다(HB_DEAD_MS). ⚠ 호스트의 박동 주기를 늘리면 이 문턱도 함께 늘려야 한다. ⚠ 새 화면이 «프레임으로만 최신이 되는» 것을 들고 있게 되면 재접속 목록에도 넣어야 한다 — 안 넣으면 맥에서는 멀쩡하고 폰에서만 조용히 낡는다(이 버그의 모양 그대로다). 스모크가 «오프라인 동안 todo.md 를 고치고 → 다시 붙이면 그 줄이 화면에 온다» 를 잰다(고치기 전 판으로 돌려 보면 실제로 빨개진다 — 확인함).
v0.5.5 (2026-09-13) — 표식은 이름 줄로(C 안) · 키보드 문턱의 네 번째 사고 · 원격에는 «메인 것» 을 안 보여 준다: ① 표식 위치 (V24 C 안, Dave 선택) — 폴더봇은 오른쪽 아래 모서리에 상태 배지를 이미 그린다(FolderBot.tsx: "레일 18px 에서도 읽힌다. 그래서 행 옆의 별도 점은 뺐다"). 내가 회사 표식을 같은 모서리에 붙여 실행 중·확인 대기에서 표식이 배지에 먹혔다. 네 안(대각선·탭·둘레 링·이름 줄)을 실제 크기·실제 줄에서 그려 Dave 가 이름 줄을 골랐다. 🔴 모서리는 «시간이 걸린 신호(상태)» 의 자리이고, 회사는 안 변하는 정체라 조용한 자리로 물러난다 — 아이콘 모서리에 다시 붙이는 변경은 이 결정과 충돌한다(스모크가 .bface 부활을 잡는다). ⚠ 이름 칸은 말줄임이 걸린 자리라 표식은 Mid 의 형제로 둔다(안에 넣으면 같이 잘린다). 배지는 그대로 둔다 — «표정이 있는데 배지가 필요하냐» 는 물음에 대한 답은 «크기가 다르다» 다: 눈은 64 좌표계의 6×10 이라 24px 에서 2.2×3.8px 이고, 그 크기에서 실눈과 보통 눈은 구분되지 않는다. 색은 형태가 안 읽히는 크기에서도 읽힌다. ② 키보드 문턱 — 네 번째 (Dave: "키보드랑 입력창 사이에 여백이 너무 넓어"). 레이아웃 판정은 r0.4.8 에서 «입력 중이냐» 로 고쳤는데, 화장(머리·모델 칩 접기)에는 문턱(covered > 140)이 그대로 남아 있었다. 홈 화면 앱·타사 키보드에서는 레이아웃 뷰포트까지 함께 줄어 innerHeight − vv.height 가 0 이라 그 문턱이 «안 열렸다» 로 떨어진다. 그래서 칩이 안 접히고 .chat-foot 이 --sab(홈 인디케이터 34pt)를 계속 비워 둬 입력창과 키보드 사이가 통째로 떴다 — 이미 키보드가 덮은 자리를 비워 둔 것이다. 🔴 화장에도 문턱을 쓰지 않는다. 판정은 하나뿐이다. ⚠ 위아래 화살표·체크 막대는 iOS 가 그리는 입력 도우미 막대라 웹에서 없앨 수 없다(네이티브 WKWebView 라면 inputAccessoryView = nil; Safari·PWA 에는 그 API 가 없다) — 할 수 있는 건 그 위를 안 낭비하는 것뿐이다. ③ 원격/메인 가르기 (Dave: "원격에서는 메인에 필요없는 내용들은 보여질 필요가 없어") — 기준은 «이 기기에서 할 수 있나». 눌러도 아무 일이 안 일어나는 줄은 원격에서 줄 자체를 안 그린다: 페어링 코드(호스트가 루프백에서만 연다) · macOS 권한(볼트를 읽는 메인 맥의 것인데 원격 맥 앱에도 folderbotDesktop 이 있어 새고 있었다) · 훅 설치(메인의 ~/.claude/settings.json 을 고친다 — 원격에는 상태만). 반대로 «메인을 원격에서 고치는» 것이 존재 이유인 줄(장기 토큰 · 기기 끊기)은 남긴다 — 헤드리스 메인을 밖에서 손보라고 만든 기능이다. ⚠ 새 줄을 만들 때 «메인에서만 되는가» 를 먼저 정한다 — 안 정하면 원격 화면에 죽은 버튼이 하나 는다.
v0.5.6 (2026-09-13) — 목업 먼저(계약) · 실행 중에도 호버 확대 · 지침·하네스 줄 살리기: ① CLAUDE.md 신설 — «목업 먼저, 제품은 그 다음» (Dave 확정: "앞으로 항상 목업 먼저 그리고 제품 만들 거야"). 화면에 보이는 것을 바꾸는 일은 코드를 쓰기 전에 /design 으로 먼저 보여 주고, 방향이 갈리면 안을 2~4개 내되 각각에 솔직한 대가를 적는다(내 favorite 에만 근거를 붙이면 고르는 게 아니라 짜고 치는 것이다). ⛔ «간단해 보여서» 로 건너뛰지 않는다 — 이 규칙이 생긴 이유가 그거다: 표식을 아이콘 모서리에 붙이는 «한 줄» 이 상태 배지와 겹쳐 두 라운드를 먹었다. 목업 없이 바로 고쳐도 되는 것(버그 · 승인된 목업의 구현 · 안 보이는 것 · 지정 변경)도 함께 못박아 범위가 번지지 않게 했다. ② 실행 중인 폴더봇이 호버에 안 커지던 것 (Dave: "애니메이션이 동작하는 폴더봇은 마우스 오버를 할 때 확대가 안 되네") — 🔴 돌고 있는 CSS 애니메이션은 같은 속성의 일반 선언을 늘 이긴다. .fb-work 가 transform 으로 흔들고 있어서 호버의 transform:scale(1.45) 이 통째로 무시됐다(그래서 실행 중인 봇만 안 커졌다). bob 을 개별 속성 translate 로 바꾸고 호버를 scale 로 주었다 — 둘은 독립 속성이라 겹치지 않고, 흔들면서 커진다. ⛔ 둘 중 하나를 transform 으로 되돌리면 그 순간 다시 겹친다. ③ 패널 «지침 · 하네스» 가 깨져 있던 것 (Dave 스크린샷) — 원인은 단순하다: 컴포넌트에 .hz·.hsec 클래스만 붙이고 CSS 를 안 썼다. 규칙이 없으니 아이콘이 제 줄로 빠지고 이름과 범위가 «CLAUDE.md지침 · 폴더» 로 붙었다. 패널의 .kv 와 같은 리듬(한 줄 · 26px · 말줄임 · 범위는 오른쪽 끝)으로 채웠고, «모두 보기» 가 스킬 88개까지 가므로 max-height:46vh 로 그 안에서 스크롤하게 했다. ⚠ 세 검사 모두 고치기 전 판에서 실제로 빨개지는 것을 확인하고 넣었다 — 초록인 검사를 넣고 «회귀 방어 완료» 라고 적는 것이 가장 흔한 거짓말이다.
v0.5.7 (2026-09-13) — 쓰다 만 메시지는 안 날아간다: Dave — "작성중인 채팅 텍스트 메시지가 앱을 껐다가 켜면 날라가." 입력창의 글은 useState('') 하나여서 새로고침·재시작에 그대로 사라졌다. 이제 localStorage 에 fb:draft:<botId>:<sessionId> 로 담고 300ms 뒤 저장한다. 🔴 세션마다 따로 둔다 — 하나로 두면 세션을 바꿔 놓고 돌아왔을 때 남의 초안이 입력창에 들어와 있고, 그걸 모른 채 엔터를 치면 엉뚱한 대화에 엉뚱한 말이 간다. 아직 세션이 없으면 new 로 담았다가 첫 메시지에서 지워진다(그 순간 키가 new → 실제 id 로 바뀌므로 둘 다 명시적으로 치운다 — 지연 저장이 새 키에 대고 지우는 경합이 있다). ⚠ 첨부도 함께 저장한다 — 본문의 @파일 만 살아나고 첨부가 비면 파일 없이 보내진다(조용히 틀린다). ⚠ localStorage 는 사생활 보호 창·저장 차단에서 던지므로 읽기·쓰기를 전부 try 로 감쌌다 — 초안 보관은 편의지 입력의 전제가 아니다. ⚠ 키를 바꾸는 이펙트가 setText 보다 먼저 draftRef 를 옮긴다(안 그러면 저장 이펙트가 옛 글을 새 키에 쓴다). 스모크가 «치고 → 새로고침 → 돌아오는가 → 비우면 키가 지워지는가» 를 재고, 기능을 빼면 실제로 빨개진다(확인함).
v0.5.8 (2026-09-13) — 채팅 「A · 문서처럼」 (Dave 확정): 세 방향(문서처럼 · 카드 · 검은 유리)을 목업으로 내고 Dave 가 A 를 골랐다. 요구가 둘이었고 서로 당겼다 — "Rondo 처럼 화려해지지 않게" + "단순함을 지키되 고급스럽게". 🔴 «고급스럽게» 를 더하기로 풀면 반드시 앞의 요구와 부딪힌다. 그래서 빼기로 풀었다. ① 기계는 언제나 접힌다 — 도구를 한 번만 써도 한 줄(도구 N회 · 파일 M개)이다. 종전에는 «4회 이상일 때만» 묶어 짧은 턴일수록 대화가 로그처럼 보였다. ⛔ 문턱으로 접기를 정하지 않는다 — «몇 개부터 묶나» 는 늘 틀린 질문이고, 기계는 언제나 접히고 펼치는 것은 사람이 정한다(문턱으로 판정해 네 번 틀린 키보드와 같은 병이다). ⛔ 접힌 줄에 도구 이름을 늘어놓지 않는다(Read 3 · Bash 2) — 그 줄이 길어지는 만큼 대화가 로그가 된다. 접힌 상태의 질문은 «얼마나» 이고 «무엇을» 은 펼쳤을 때 답한다. ⚠ 실패만은 접힌 채로도 말한다 — 조용히 접으면 사람이 실패를 영영 못 본다. ② 상자는 «차례» 를 뜻한다 — 사람 말의 배경 상자를 걷어냈다. 배경·테두리는 답해야 진행되는 것(승인·에이전트의 질문)에만 남긴다. 상자를 아껴야 보이는 순간 눈이 간다. ③ 읽기 폭 58ch · 행간 1.85 · 턴 사이 26px — 넓은 화면에서 줄이 끝까지 늘어나면 한 줄을 눈으로 되짚게 된다. 고급스러움이 가장 크게 갈리는 지점이다. ⚠ A 의 약점(턴 경계가 약하다)은 실재한다 — 여백과 1px 선 하나로만 푼다. 선을 굵히거나 배경을 깔면 그 순간 B(카드)가 된다. ⚠ 접기의 대가는 도는 동안의 상태 한 줄(.live)이 갚는다 — 그게 유일한 장치라 지우면 안 된다. 스모크가 «사람 말에 상자가 없다 · 오른쪽 정렬 · 리듬 26px · 읽기 폭 · 도구가 펴져 있지 않다» 를 재고, 되돌리면 실제로 빨개진다(확인함).
v0.5.9 (2026-09-13) — 답변 속 경로가 칩이 된다 · 문서 읽기 폭: A 안 2라운드. ① 채팅에서 문서로 바로 (Dave: "채팅에서 문서 선택으로 바로 문서로 이동") — 봇의 답 안에서 경로처럼 보이는 글자를 눌러서 여는 칩으로 만든다. 🔴 실제로 있는 것만 칩이 된다 — 후보는 core/paths.ts 가 내고 있는지는 호스트가 답한다(POST /api/bots/:id/exists, 한 번에 40개까지·루트 밖은 guard 가 막는다). 확인 없이 만들면 죽은 링크가 대화에 쌓이고, 한 번 눌러 본 사람은 다시 안 누른다. 🔴 렌더된 HTML 문자열을 정규식으로 건드리지 않는다 — 태그 안쪽을 잘못 물면 마크업이 깨진다. 그린 뒤에 텍스트 노드만 걸어 바꾸고 code·pre·a 안은 건너뛴다. ⚠ 스트리밍 중에는 하지 않는다(글자가 바뀌는 동안 DOM 을 갈면 선택이 튄다). ⚠ PARA 폴더 이름에는 공백이 있다(3. Area) — 공백을 통째로 허용하면 앞 문장까지 삼키므로 공백 없는 뼈대를 먼저 잡고 왼쪽으로 두 낱말까지 넓힌 변형을 함께 낸 뒤, 어느 쪽이 진짜인지는 호스트가 고르게 했다(한 자리에서 여러 개가 걸리면 긴 것이 이긴다). 유닛 11건 + 스모크가 «있는 파일은 칩이 되고 없는 경로는 글자로 남는다» 를 재며, 배선을 끊으면 실제로 빨개진다(확인함). ② 문서 읽기 폭 58ch · 행간 1.9 — 채팅과 같은 활자 사다리를 문서에도 적용. 탭의 밑줄·미리보기 기울임은 이미 A 안 그대로여서 손대지 않았다. ⚠ 목업과 다르게 간 것 하나 — 목업은 봇 답의 첫 줄을 제목처럼 그렸지만, 그건 봇이 스스로 쓴 문장이지 우리가 붙일 수 있는 위계가 아니다. 없는 제목을 지어내느니 활자·여백만으로 갈랐다.
v0.6.0 (2026-09-13) — 라이브 프리뷰 마크다운 편집기 (문서 기능 A · 1단계): 마크다운을 서식이 보이는 채로 그 자리에서 고친다. Rondo 알파의 편집기를 옮기는 첫 단계로 CodeMirror 6 + @codemirror/lang-markdown 을 올리고 src/client/MdEditor.tsx 를 만들었다. 🔴 churn 0 — 열고 저장해도 바이트가 그대로다. 공짜인 이유는 파싱도 직렬화도 안 하기 때문이다: 문서는 CodeMirror 의 문자열 그대로이고 데코레이션은 «보여주기» 일 뿐 문자를 하나도 안 바꾼다. ⛔ «마크다운을 파싱해서 다시 써 주는» 기능을 여기 붙이면 그 순간 이 계약이 깨진다. 스모크가 프론트매터·강조·목록·인용·위키링크가 섞인 파일을 열어 한 글자 쳤다 지우고 바이트를 비교한다. 🔴 커서가 없는 줄의 마커만 숨긴다 — 숨김은 Decoration.replace({}) 라 문자가 doc 에 그대로 남아 선택·복사·좌표가 순정 textarea 와 같다. 그래서 «모드» 가 없다. 🔴 숨긴 마커 위에 커서가 서면 서식이 조용히 깨진다 — 숨긴 범위를 atomicRanges 로 원자화하고, 그래도 떨어지는 경우를 transactionFilter 가 밀어낸다. 두 겹이 필요하다(원자 범위만으로는 클릭으로 들어오는 캐럿을 못 막는다). 제목 크기는 줄 클래스(.lp-h1)로 준다 — 토큰에 걸면 # 를 치는 순간에는 아직 안 커진다. CRLF 는 표시용으로 \n 으로 맞추고 저장할 때 되돌린다(혼합 EOL 은 LF 수렴 — 한계로 남긴다). ⚠ 지연 로드가 계약이다 — 편집기는 511KB 짜리 별도 청크이고 본체는 438KB 그대로다. 문서를 한 번도 안 연 폰이 마크다운 파서를 받으면 안 된다. 스모크가 «편집기가 별도 청크인가 · 본체에 CodeMirror 가 섞였나» 를 잰다. 🔴 이 라운드가 잡은 진짜 버그: 처음에는 편집기 생성 이펙트의 의존성에 value 를 뒀는데, onChange 가 부모의 draft 를 올리면 그 value 가 바뀌어 한 글자 칠 때마다 편집기가 통째로 다시 만들어졌다 — 포커스·커서·되돌리기가 그때마다 날아간다. 스모크가 «Backspace 가 안 먹는다» 로 잡았고, 재 보니 포커스가 body 로 빠져 있었다. ⛔ 그 이펙트에 value 를 다시 넣지 마라 — 밖에서 바뀐 글은 별도 이펙트가 넣는다. ⚠ 아직 위젯이 없다(표 리치 에디터 · 프론트매터 속성 패널 · 콜아웃 카드 · 이미지 임베드) — 2단계다.
v0.6.1 (2026-09-13) — 편집기 위젯: 체크박스 · 위키링크 · 이미지 (문서 기능 A · 2단계): 파서가 모르는 것들은 줄을 직접 훑는다 — lezer 는 [[ ]] 를 모르고, 체크박스는 위치가 정확해야 해서 줄 스캔이 더 맞다. ① 체크박스 — 눌러서 [ ] ↔ [x] 를 바꾼다. 🔴 한 글자만 갈아야 한다. 줄을 다시 쓰면 churn 이 나고 커서·되돌리기가 튄다. 스모크가 «누른 뒤 파일 길이가 그대로이고 다른 글자가 정확히 1개» 인지 재고, 다시 눌러 원상복구되는 것까지 본다 — Folder Bot 은 todo.md 가 중심이라 이 한 줄이 제품의 핵심 동작이다. ② 위키링크 — 대괄호는 숨기고 글자만 링크로(Rondo 의 확정 표기), 누르면 문서 탭에서 연다. ③ 단독 줄 이미지 —  만 있는 줄은 그림으로 바꾼다. 경로는 /api/bots/:id/raw 로 돌려 폰·브라우저에서도 뜬다. ⚠ 셋 다 커서가 그 줄에 들어오면 원문으로 돌아간다(체크박스만 예외 — 위치가 고정이라 늘 위젯이다). ⚠ 아직 표 리치 에디터와 프론트매터 속성 패널이 없다 — Rondo 에서 표만 690줄로 이식 대상 중 가장 큰 덩어리다. 3단계다.
v0.6.17 (2026-09-13) — Finder 급 파일 조작 (A1~A5): 트리 우클릭에 Finder 에서 보기 · 새 노트 · 새 폴더 · 복제 · 이미지 복사 · 경로 복사(절대/폴더 기준) 를 넣었다. ⚠ 「Finder 에서 보기」는 「기본 앱으로 열기」와 다른 일이다 — 저것은 파일을 열고 이것은 어디 있는지 보여 준다(open -R). 파일을 옮기거나 다른 앱에 끌어다 놓으려는 사람이 원하는 것은 늘 이쪽이라 따로 뒀다. ⚠ .md 는 자동으로 붙인다 — 사람이 확장자를 기억하게 하지 않는다. ⚠ 만드는 자리는 «누른 것이 폴더면 그 안, 파일이면 그 옆». 🔴 덮어쓰지 않는다 — 같은 이름이 있으면 이름 2 로 비킨다(새로 만들기·복제는 한 번 누르면 끝인 동작이라 덮어쓰면 되돌릴 방법이 없다). 🔴 이미지는 «그림 그대로» 클립보드에 넣는다 — 경로를 복사해 봐야 붙여넣는 쪽은 글자를 받는다. ⚠ 브라우저가 받아 주는 것은 PNG 뿐이라 다른 형식은 캔버스로 한 번 굽는다. 🔴 새로 만드는 것은 guard(roots(bot)) 로 모자랐다 — roots 에는 볼트 루트·참조 폴더가 들어 있어서 dir: '../..' 가 통과했다(스모크가 잡았다). 읽기는 넓게 허용해도 새로 만드는 것은 자기 폴더 안이어야 해서 inBot() 으로 따로 막는다.
v0.6.16 (2026-09-13) — Canvas 를 통째로 · 서식은 외우지 않아도 된다: ① Obsidian 의 .canvas 를 보고 고친다 (Dave: «편집 기능까지 다 만들어줘») — JSON Canvas 1.0: text·file·link·group 노드와 엣지(화살표·색·라벨), 팬·줌·열 때 맞춤, 노드 끌기·크기 조절·면에서 끌어 엣지 잇기·글 상자 더하기·지우기·색. 🔴 모르는 필드를 버리지 않는다 — 우리가 모르는 값도 Obsidian 에는 뜻이 있어서, 읽을 때 들고 있다가 저장할 때 그대로 돌려준다. 저장 모양도 탭 들여쓰기로 맞춘다(다르게 쓰면 열기만 해도 파일이 통째로 바뀐 diff 가 난다). 🔴 화면 변환은 판 하나의 transform 뿐 — 노드마다 좌표를 다시 계산하지 않는다. ⚠ 그룹을 옮기면 안에 완전히 들어 있는 노드가 함께 간다(Obsidian 동작). ⚠ 저장은 손을 뗀 뒤에만 — 끄는 동안 매 프레임 저장하면 파일이 수백 번 쓰인다. 갈래는 kindOf 가 .canvas 를 TEXT_EXT 보다 먼저 가른다(안 그러면 JSON 원문이 보인다). ② 서식 — 마크다운 글자를 외우지 않아도 된다 (Dave: «# 같은 마크다운 단축어, 선택시 보이는 상단 메뉴»): 줄 앞에서 / 를 치면 제목·체크박스·목록·표·인용·콜아웃·코드·구분선 메뉴가 뜨고, 글을 고르면 그 위에 서식 막대(H 순환 · B · I · S · 코드 · 링크 · 인용 · 체크박스)가 뜬다. ⌘B·⌘I·⌘K 도 붙였다. 🔴 단추가 하는 일은 «글자 넣기» 뿐 — 파일에는 **굵게** 가 그대로 들어가서 churn 0 이 그대로 산다. Obsidian 의 콜아웃(> [!note])도 표시를 숨기고 줄에 색을 준다. ⛔ CodeMirror 의 update() 안에서 화면 치수를 읽지 않는다 — coordsAtPos 를 거기서 불렀더니 «Reading the editor layout isn't allowed during an update» 로 플러그인이 통째로 내려가 막대가 조용히 사라졌다. 재는 것은 requestMeasure 의 read, 고치는 것은 write 다. ⚠ / 메뉴의 후보 범위는 슬래시 다음부터다 — 슬래시부터로 주면 걸러 낼 글자에 / 가 들어가 라벨과 하나도 안 맞고 목록이 통째로 빈다(그래서 메뉴가 안 떴다).
v0.6.15 (2026-09-13) — 팝업은 절을 넘어서 · 일하는 폴더봇 · ↑↓ 는 보고 있는 칸의 것 · --- 는 가로줄: ① 🔴 팝업이 잘렸다 (Dave 제보 · 세션 벤더 고르기). 패널의 절(.sec)은 접었다 폈다 하는 칸이라 overflow:hidden 으로 자기 높이 안에서 자르는데, 그 안에서 연 메뉴도 같이 잘린다. ⛔ z-index 로는 못 푼다 — 잘림은 쌓임 순서가 아니라 부모의 자르기라 부모 밖으로 나가는 수밖에 없다. Float(포털 + position:fixed)를 만들어 레일 우클릭 · 트리 우클릭 · 봇 메뉴 · 세션 벤더 고르기 · 문서 ⋯ 를 전부 body 로 올렸다. ⚠ 좌표는 열 때 한 번 재서 고정하므로 스크롤·리사이즈에는 닫는다 — 엉뚱한 자리에 떠 있는 메뉴보다 닫힌 메뉴가 낫다. ② 진행 줄을 뒤집었다 (승인된 채팅 v2 목업). 폴더봇 · 2m 48s · 짧은 행동 순서에 중단은 오른쪽 끝, 물결은 바로 위 접힌 줄로 옮겼다. 🔴 마스코트가 상황에 따라 바뀐다 (Dave 확정 — 셋 중 하나를 고르는 게 아니라 셋을 다 쓴다): 생각·명령은 숨쉬기, 읽기·쓰기·찾기는 서류 넘기기, 답 쓰기는 타이핑. 판정과 말은 순수 함수(core/work.ts)라 유닛이 고정한다 — ⛔ 진행 줄에 도구·파일 이름을 쓰지 않는다(줄이 길어지는 만큼 대화가 로그가 된다). 맥박 점(·)은 뺐다 — 마스코트가 이미 같은 말을 한다. ③ 🔴 ↑↓ 가 문서 안에서 파일 선택을 움직였다 (Dave 재보고). 종전 판정이 «textarea·input 이 아니면 글 쓰는 중이 아니다» 였는데 편집기는 contenteditable 이라 그 그물에 안 걸렸다. 이제 두 겹으로 본다 — 글 쓰는 중인가(isContentEditable 포함) · 포커스가 문서 열 안인가. ⌘F 도 같은 규칙을 쓴다(문서면 편집기 찾기, 파일 칸이면 이름 거르기). ④ --- 가 가로줄이 된다 — 줄 전체가 마커라 통째로 바꾼다. ⚠ 프론트매터의 --- 는 구분선이 아니라 경계라 건드리지 않는다.
v0.6.14 (2026-09-13) — 누른 자리에 커서 · 링크 파비콘 · 외부 열기 아이콘 · 메뉴바 게이지: ① 🔴 문서를 처음 클릭하면 커서가 엉뚱한 데 섰다 (Dave 제보). 원인은 «커서가 든 줄은 원문을 드러낸다» 는 규칙 자체다 — 드러나는 순간 줄이 통째로 오른쪽으로 밀리는데, 클릭 위치는 밀리기 직전 배치로 정해진다. 그래서 Rondo 처럼(livePreview.tsx 2455~) 서식 마커를 커서가 있어도 계속 숨긴다: ## > ** ~~ 백틱. ⚠ # 만 친 상태(뒤에 공백 없음)는 그대로 보여 준다 — 아직 제목이 아니고 사람이 서식에 들어서는 중이다. ⛔ 주소(URL)는 예외로 커서 줄에서 드러낸다 — 그건 서식이 아니라 내용이라 안 보이면 오타 난 주소를 고칠 길이 없다. 곁들여 맨 URL 이 통째로 사라지던 버그를 고쳤다(URL 노드를 무조건 숨기고 있었는데, [글](주소) 의 주소와 달리 맨 URL 은 그 자체가 보이는 전부다 — 여는 괄호 뒤인지로 가른다). 그리고 이미지 높이를 기억한다 — 늦게 도착한 그림이 아래 글을 밀어 같은 어긋남을 만들었다. 스모크는 «커서가 어느 글자냐» 가 아니라 줄이 안 밀리느냐를 잰다(커서는 원래 가장 가까운 글자 경계로 붙는다 — 제목은 한 글자가 24px 라 그만큼 떨어진다). ② 링크 앞 파비콘 — 채팅 · 문서 편집기 · 입력창이 같은 캐시 하나를 본다(각자 받으면 한 화면 안에서 아이콘이 서로 다른 순간에 나타난다). 받아 오는 것은 호스트다: 화면이 폰일 수도 원격 맥일 수도 있는데 각자 받으면 기기 수만큼 두드리고 폰이 볼트 밖 사이트를 직접 찌른다. ⚠ 먼저 /favicon.ico 를 찔러 보고 없을 때만 문서를 받아 <link rel=icon> 을 고른다 — 빠르고 우리가 보는 것도 적다. 없다는 사실도 캐시한다. ⚠ 입력창은 textarea 라 안에 그림을 못 넣는다 — 쓰는 중인 주소를 칸 위 칩으로 올려 보내기 전에 확인할 수 있게 했다. ⚠ 어디서나 자리표시자(지구본)를 먼저 놓는다 — 비워 두면 아이콘이 도착할 때마다 글줄이 밀린다. ③ 외부 앱으로 열기를 ⋯ 밖으로 꺼냈다. ④ 🔴 원격 맥의 메뉴에 사용량이 아예 없었다 — pollUsage 를 startHostMode() 안에서만 시작해서, 호스트에 붙어 쓰는 맥은 영영 안 받아 왔다(받아 오는 쪽은 원래부터 두 모드를 알고 있었다). 이제 모드와 무관하게 돌고, 메뉴와 메뉴바 제목에 막대 게이지를 그린다 — 네이티브 메뉴는 HTML 을 못 그리므로 폭이 고른 블록 문자(▰▱)가 정답이다.
v0.6.13 (2026-09-13) — 채팅에 쓴 한 줄이 그대로 루틴이 된다: Dave: «루틴을 폴더 채팅에서 바로 채팅으로 생성할 수 있으면 좋겠어». 컴포저의 + → 「루틴으로 만들기」 를 누르면 방금 쓴 문장에서 주기·이름·시킬 일을 뽑아 루틴 편집 화면이 채워진 채로 뜬다. ⛔ cron 을 사람이 외우게 하지 않는다 — 「매주 월요일 아침 8시에 지난주 한 일 정리해 줘」 가 0 8 * * 1 이다(core/routineText.ts 의 순수 함수, 유닛 4건). 시각 말(새벽·아침·점심·오후·저녁·밤), 오전/오후, 분, 평일·주말·요일·매월 N일을 읽는다. 🔴 저장은 사람이 누른다. 주기는 어디까지나 추측이고, 틀린 추측을 조용히 저장하면 엉뚱한 시각에 봇이 혼자 일한다 — 그래서 하는 일은 «폼을 미리 채워 주는 것» 까지다(스모크가 «안 눌렀으면 저장 안 됨» 을 잰다). ⚠ 못 읽으면 매일 오전 9시로 두되 matched: false 로 알린다 — 틀린 값을 맞은 척하지 않는다. ⚠ 입력칸이 비어 있으면 직전에 보낸 말을 쓴다(「아까 그거 매일 해 줘」 가 자연스러운 흐름이다). + 메뉴의 설명 줄에 읽어낸 주기(「매주 월요일 08:00」)를 미리 보여 줘서, 누르기 전에 맞는지 알 수 있다.
v0.6.12 (2026-09-13) — 맥 기본 단축키를 전 영역에: ⌘, 설정 · ⌘/ 단축키 표 · ⌘K 폴더 고르기 · ⌘N 새 세션 · ⌘1…9 n번째 폴더 · ⌘[ ⌘] 이전·다음 폴더 · ⌘⇧U 알림 (기존 ⌘B · ⌘⇧B · ⌘⇧D · ⌘W 유지), ⎋ 로 시트 닫기. 🔴 전부 ⌘(⌃) 를 낀다 — 맨 글자 단축키를 두면 입력칸에 글자를 치는 순간 명령이 돈다(이 앱은 거의 모든 화면이 글 쓰는 화면이다). 스모크가 «글자를 쳐도 명령이 안 돈다» 를 함께 잰다. ⛔ 텍스트 편집 단축키(⌘Z·⌘C·⌘V·⌘A·⌘F)는 가로채지 않는다 — 맥 기본과 편집기(CodeMirror)의 것이 이겨야 한다. 가로채는 순간 «실행 취소가 안 되는 앱» 이 된다. ⚠ 표가 정본이다 — KEYS 한 배열이 동작과 ⌘/ 화면을 함께 먹여서, 표와 실제가 갈리지 않는다. 곁들여 맥 메뉴바를 붙였다(Menu.setApplicationMenu) — 맥 앱이면 메뉴가 있어야 하고, 없으면 사람은 «이 앱엔 단축키가 없나» 로 읽는다. ⛔ 편집 메뉴는 role 로만 둔다(undo·redo·cut·copy·paste·selectAll) — 손으로 만들면 그 순간 맥 기본 동작이 깨진다. 우리 항목은 fb:cmd 로 화면에 넘겨 동작을 한 곳에 둔다.
v0.6.11 (2026-09-13) — Codex 를 Claude 와 같은 높이로: Dave: «현재 codex 실행 반영이 전혀 안 되어 있어. codex도 claude와 같이 영구 토큰 에이전트 모델 기본 설정등이 있어야 해». ① 기본값을 벤더마다 따로 둔다 — 🔴 하나로 두면 Codex 세션이 claude-fable-5-1 로 떠서 첫 턴에 죽는다(이름 체계가 다르다). sessions.defaults 가 {claude, codex} 가 되고, 세션은 자기 벤더 칸에서 값을 가져간다. ② 노력은 플래그가 아니라 설정 키다 — Codex 에는 --effort 가 없고 -c model_reasoning_effort="…" 로 준다. ⛔ 아는 값만 넘긴다: Claude 의 xhigh·max 를 그대로 넘기면 CLI 가 그 자리에서 죽으므로 모르면 안 넘기고 CLI 기본값에 맡긴다(같은 규칙을 모델 이름에도 쓴다 — fitsProvider). ③ 샌드박스가 곧 권한 정책이다 — Codex 에는 stdio 권한 프로토콜이 없어 우리가 승인 화면을 못 띄운다. 그래서 설정에 「읽기만 · 이 폴더 쓰기 · 전부 허용」 을 두고 그 값이 --sandbox 로 간다. ④ 영구 토큰의 Codex 판 — codex login 은 브라우저를 여는 대화형이라 우리가 대신 못 한다. 그래서 상태만 읽고(~/.codex/auth.json · OPENAI_API_KEY), 헤드리스면 API 키를 받아 워커 환경에만 넣는다(⛔ 사용자의 ~/.codex 설정 파일은 건드리지 않는다). ⑤ 화면의 모델·노력 목록은 그 세션의 벤더가 정한다. Codex 모델은 CLI 판마다 달라서 직접 입력을 함께 준다 — 우리가 목록을 «정하지» 않는다. ⚠ 스모크는 가짜 CLI 가 남긴 argv 로그로 모델·노력·샌드박스·키가 CLI 까지 실제로 갔는지 잰다(가짜는 플래그를 안 쓰므로 화면에는 흔적이 없다). 🔴 곁들여 포트 선점 가드를 넣었다 — 앞선 실패로 남은 호스트가 같은 포트를 잡고 있으면 우리는 «건강한 응답» 을 받고 옛 코드를 재게 된다(이번에 실제로 그래서, 고친 것이 안 고쳐진 것처럼 보였다).
v0.6.10 (2026-09-13) — 레일 우클릭으로 폴더 삭제: 좌측 폴더 레일에서 우클릭하면 정지 · 은퇴 · 폴더 삭제가 나온다 (Dave 요청). ⚠ 셋은 서로 다른 일이라 한 항목으로 합치지 않았다 — 정지는 목록에서만 내리고(기록 유지), 은퇴는 Archive 로 보내 볼트의 일부로 남기고, 삭제는 볼트에서 치운다. 🔴 삭제는 지우지 않고 옮긴다 — 되돌릴 수 없는 일을 우클릭 한 번 뒤에 두지 않는다. 폴더는 볼트 안 .folderbot/trash/<시각>_<이름> 으로 통째로 가고, 파인더에서 꺼내면 그대로 돌아온다(확인창에 그 말을 적는다). 오케스트레이터 줄에는 이 메뉴가 없다 — 있으면 볼트 자체를 지우게 된다. ⚠ 스모크는 화면에서 지우지 않고 API 로 잰다(화면에서 지우면 뒤따르는 검사가 쓰는 봇이 사라진다). 곁들여 표 편집 검사의 고정 대기를 폴링으로 바꿨다 — 자동 저장은 «멎고 800ms 뒤» 라 느린 날에 갈렸다(실제로 한 번 갈렸다). ⛔ 「그날 운이 나빴다」로 넘기지 않는다.
v0.6.9 (2026-09-13) — 벤더는 폴더가 아니라 세션의 성질이다: 🔴 회사 표식을 왼쪽 폴더 레일에서 떼어 세션 목록으로 옮겼다 (Dave: «해당 폴더에는 여러 세션이 claude 혹은 chatgpt 구분없이 생길 수 있기 때문»). 그래서 모델이 바뀐다 — ① SessionInfo.vendor 신설, 워커 종류는 이제 세션이 정하고 봇의 값은 «안 고른 세션» 의 폴백이다. ② POST /bots/:id/sessions 와 /bots/:id/send 가 vendor 를 받는다. ③ 폴더 하나 = 줄 하나 — 종전에는 «이미 있나» 를 rel + vendor 로 봐서 같은 폴더가 Claude/Codex 두 줄로 섰다(V24 의 «형제»). 이제 rel 만 본다. ⛔ 폴더 이름 뒤의 «· Codex» 도 뺐다 — 폴더 이름에 회사를 박으면 그 폴더가 한 회사 것처럼 보인다. ④ 세션 목록의 + 가 Claude/ChatGPT 를 묻는다. 세션이 0 이면 종전의 「메시지를 보내면 생겨요」 는 막다른 안내였다(누가 맡을지 고를 자리가 어디에도 없었다) — 이제 그 자리가 「Claude 나 ChatGPT 로 시작」 단추다. ⚠ 깔린 에이전트가 하나면 묻지 않는다. 채팅 머리의 표식도 폴더가 아니라 지금 이 세션의 것을 그린다. 스모크는 «벤더가 달라도 봇은 하나» · «한 폴더 안에 Claude 세션과 Codex 세션이 섞여 산다» 를 잰다.
v0.6.8 (2026-09-13) — 보는 화면이 곧 고치는 화면 · 채팅은 cursor 스타일로 되돌림: ① 🔴 문서에서 「편집 모드」를 없앴다 (Dave: «rondo 와 같이 보기화면과 편집화면이 완벽하게 싱크»). 마크다운은 언제나 라이브 프리뷰 편집기 하나로 그린다 — 눈·연필 토글(v0.6.4)도, 더블클릭 진입도, 「편집 중」 배지도 함께 지웠다. ⛔ 읽기용 Md(marked)와 편집용 편집기를 둘 다 두지 않는다 — 둘을 오가는 순간 «싱크» 가 사람 몫이 되고, 실제로 그 왕복에서 «떠날 때의 저장이 새 문서를 덮는» 사고가 났다(v0.6.3). 모드가 없으니 알릴 상태도 없어서 저장 중·실패일 때만 한 마디 한다. ⚠ 잘려서 받은 파일(truncated)만 읽기 전용으로 남긴다 — 앞부분만 들고 저장하면 뒷부분이 날아간다. 「편집 중」 대신 「아직 안 낸 글이 있다」(dirty) 로 충돌 배너를 판단한다. ② 채팅 외양을 cursor 스타일로 되돌렸다 (Dave: «이전 스타일이 더 나»). 「A · 문서처럼」의 26px 리듬 · 오른쪽 정렬 · 58ch · 머리줄(17/600) · 턴 경계선을 전부 되돌리고, 사람 말은 다시 입력칸 모양 상자로 답 위에 놓인다. ✅ 남긴 것은 둘뿐 — 기계 접기(.mach) 와 문서 링크칩(.pchip). Dave 가 콕 집어 원한 것이 링크칩이고, 기계 접기는 «Rondo 처럼 화려해지지 않게» 라는 요구 그 자체라서 남겼다(외양이 아니라 조용함의 장치다).
v0.6.7 (2026-09-13) — 도망가는 ↓ · 라이트 테마의 묻힌 글자 · 바깥 링크는 바깥에서: ① 🔴 「최근으로」 단추가 누르려 하면 도망갔다 (Dave 제보). 원인은 자동 스크롤과 단추가 문턱 하나(80px)를 같이 쓴 것 — 글이 흘러드는 동안 그 선을 오가며 단추가 DOM 에서 빠졌다 들어왔고, 들어올 때마다 «4px 떠오르는» 등장 애니메이션이 다시 돌았다. 이제 눈금을 나눈다: 자동 스크롤은 그대로 80px, 단추는 240px 넘게 멀어져야 뜨고 40px 안에 와야 사라지는 이력(hysteresis)을 쓰며, 사라질 때도 DOM 에서 빼지 않고 투명도만 낮춘다. 스모크가 마우스를 올린 전후 좌표가 같은지 잰다. ② 🔴 라이트 테마에서 「여기서 시작」 이 안 보였다 — .btn.primary 가 color:#111 을 박아 뒀는데 라이트에서는 --w 가 바로 그 #111 이라 배경과 글자가 같은 색이 됐다(hover 의 #fff 도 같은 문제). 「글자 위 색」은 언제나 --onAccent 로 바꿨고, 라이트 테마에서 칠해진 단추를 전수로 훑어 대비 3:1 미만이면 빨개지는 검사를 붙였다(그 검사가 .sendb.off 2.38 도 함께 잡아 고쳤다). ③ 바깥 링크는 바깥에서 연다 — setWindowOpenHandler 는 target=_blank 만 받아서, 답변 속 평범한 <a href> 는 창을 통째로 그 사이트로 끌고 갔다(앱이 그 자리에서 사라지고 돌아올 길도 없다). 셸의 will-navigate 에서 우리 화면이 아닌 http(s) 를 기본 브라우저로 넘기고, 렌더러도 그린 뒤 바깥 링크에 target=_blank 를 단다(marked 는 안 달아 준다).
v0.6.6 (2026-09-13) — 턴 경계는 1px 선 하나: 「A · 문서처럼」의 유일한 약점을 캔버스가 적어 둔 처방 그대로 메웠다 — 배경도 카드도 안 쓰기로 했으니 새 턴이 시작되는 자리(사람이 말한 자리)에 1px 선 하나만 긋는다. ⛔ 굵히거나 배경을 깔면 그 순간 B(카드)가 된다 — 스모크가 두께와 배경을 잰다. ⚠ 선이 세로 리듬을 늘리지 않게 음수 여백으로 26px 한가운데 앉힌다(폰은 14px 리듬이라 값이 다르다) — 안 그러면 턴 사이가 52px 로 벌어져 «조용함» 이 «허전함» 이 된다.
v0.6.5 (2026-09-13) — 봇 답의 첫 줄 · 접힌 줄의 걸린 시간: 승인된 캔버스의 활자 여섯 단계 중 마지막으로 남아 있던 「봇 답의 첫 줄(17/600)」을 넣었다. ⚠ 짧은 한 줄이 문단 하나로 끝날 때만 올린다 — 길면 굵은 덩어리가 돼서 오히려 시끄럽고, 뒤에 빈 줄이 없으면 스트리밍 중에 커졌다 작아졌다 한다(그래서 «뒤에 빈 줄» 이 조건에 들어 있다). ② 접힌 기계 줄에 걸린 시간을 더해 「도구 7회 · 파일 3개 · 12.4초」로 맞췄다. 도구 줄에는 «끝난 시각» 이 없어서 다음 줄이 생긴 시각으로 잰다. ⚠ 0.1초 미만은 안 적는다 — 「0.0초」 는 정보가 아니라 잡음이다. 글귀 규칙은 순수 함수 machSummary 로 빼 유닛이 고정한다(스텁은 즉답이라 화면 검사로는 시간을 잴 수 없다 — 잴 수 없는 것을 화면에서 우기지 않는다). ⛔ 접힌 줄에 도구 이름을 늘어놓지 않는다는 규칙은 그대로고, 스모크가 줄이 길어지는지 본다.
v0.6.4 (2026-09-13) — 모드 전환은 눈·연필 두 아이콘뿐: 승인된 「A · 문서처럼」 캔버스의 문서 규칙을 마저 옮겼다. 읽기↔편집은 툴바의 같은 자리 한 단추(연필 ↔ 눈)로만 오간다 — 종전의 「완료」 버튼과 «편집 중» 이라는 큰 상태 표시를 지웠다. 🔴 «원래 고칠 수 있는 것» 이 기본이라 편집을 사건처럼 알리지 않는다. 상태는 «자동 저장» 한 마디로 조용히 두고, 저장 중·실패일 때만 말을 바꾼다. ⋯ 메뉴에서도 「편집」 항목을 뺐다(같은 일을 하는 문이 둘이면 그게 곧 화려함이다). 더블클릭으로 그 자리에서 편집하는 길은 그대로다.
v0.6.3 (2026-09-13) — 표는 진짜 표로, 칸만 고친다 · 문서를 옮겨도 글이 안 따라온다: ① 표 리치 편집 — 커서가 밖에 있으면 마크다운 표를 진짜 <table> 로 그리고(정렬 :--:·--: 까지), 칸을 누르면 그 자리에서 고친다. 🔴 고친 칸의 글자만 바뀐다. Rondo 는 표를 파싱해 통째로 다시 직렬화하는데(690줄), 그러면 파이프 간격·정렬 표기가 전부 정규화돼 한 칸만 고쳐도 표 전체가 바뀐 diff 가 난다 = churn. 여기서는 그 칸의 문자 범위만 갈아 끼운다 — 스모크가 파일 전체를 바이트로 대조한다. ⛔ +행·+열도 «끼워 넣기» 뿐(순수 insert)이라 기존 문자를 옮겨 쓰지 않는다. Tab 으로 칸을 옮기고, 표 안에 커서가 있으면 접지 않는 규칙 하나로 ⋯(원문으로 고치기)가 공짜로 나온다 — 「모드」 플래그를 따로 두지 않는다. 기계(+행·+열·⋯)는 「A · 문서처럼」 대로 올려야 나온다. ② 🔴 편집 중에 다른 문서로 옮기면 그 글이 새 문서를 덮어썼다 — 실사고다. rel 이 먼저 바뀌고 edit 은 load 가 끝난 뒤에야 꺼져서, 편집기가 떠나며 내는 «못 낸 저장」이 새 문서의 onCommit 에 실렸다(스모크가 todo.md 가 표 문서로 덮인 것으로 잡았다). 문서를 옮기는 즉시 편집을 닫고, <MdEditor key={rel}> 로 문서마다 편집기를 따로 둔다 — 떠날 때의 저장이 옛 문서로 가고, 되돌리기 기록이 문서를 넘나드는 것도 함께 막힌다.
v0.6.2 (2026-09-13) — HTML 은 브라우저처럼 · 외부 앱으로 · 프론트매터 접기: ① HTML 갈래 신설 — .html 은 TEXT_EXT 에도 있어서 먼저 보지 않으면 원문으로 떨어진다(그래서 kindOf 에서 맨 앞에 뒀다). 문서 열 안에 주소줄 크롬을 얹고 페이지를 그대로 그린다. 🔴 샌드박스 안에서 그린다 — 에이전트가 만든 리포트를 앱 안에서 보되 그 안의 스크립트가 볼트를 읽거나 우리 화면을 만지지 못하게 가둔다. ⛔ allow-same-origin 을 주지 않는다 — 주는 순간 샌드박스가 사실상 없는 것과 같다. ② 외부 앱으로 열기 (POST /api/bots/:id/open) — 🔴 여는 주체는 언제나 호스트(메인 맥)다. 폰에서 눌러도 미리보기는 메인에서 뜬다. 그래서 화면이 원격이면 이름을 «메인 맥에서 열기» 로 바꾸고 «이 기기로 내려받기» 를 함께 준다 — 안 그러면 눌러도 아무 일이 없는 것처럼 보인다(우리가 세운 원격/메인 규칙 그대로). 루트 밖은 guard 가 막고 스모크가 그 차단을 잰다. ③ 프론트매터 접기 — 맨 위 --- 블록을 REFERENCE · PARA · 지침 같은 작은 라벨 한 줄로 접는다. 🔴 문서를 열자마자 YAML 이 먼저 보이면 안 된다 — 그건 문서가 아니라 설정이다. ⛔ YAML 을 파싱해서 다시 쓰지 않는다. Rondo 는 속성 패널에서 고치면 블록을 통째로 재직렬화하는데 그 순간 서식이 정규화된다(그쪽도 churn 리스크로 적어 뒀다). 여기서는 보여 주기만 하고, 고치는 것은 커서를 넣어 원문에서 한다 — 그래야 churn 0 이 예외 없이 성립한다. ⚠ 그 결과 처음 커서를 프론트매터 뒤에 둔다 — 기본값(0)이면 1번 줄이 «활성» 이라 접히지 않는다(스모크가 잡았다). 사람이 제일 먼저 고칠 곳도 거기가 아니다.