Records all key decisions made during the spec clarification process. Uses an append-only markdown log instead of Ouroboros Event Sourcing. 번호 공간은 이 문서 고유다. 상위 파이프라인의 결정은
cocode/D-005처럼 접두어를 붙여 인용한다. ⚠️ D-001~D-008 의 출처는 오너의 개별 확답이 아니라 "계속 진행 해줘"(2026-09-16) 한 마디로 Discovery §5.1 권장안을 일괄 수용한 것이다. 오너가 개별 항목을 뒤집으면 Seed Spec 진화 프로토콜로 반영한다.
Decision List
D-001: "풀스택" = Dart 풀스택 (Flutter · Jaspr · Serverpod · PostgreSQL)
- Date: 2026-09-16
- Context: 요청 원문 "풀스택 코딩 에디터"는 Dart 풀스택(H1)과 다국어(H2) 두 해석이 가능했다(Discovery §0). H2 는 상위 cocode/AC-05(Must-Not) · C-03 과 정면 충돌한다
- Options:
- H1 Dart 풀스택 — 상위 제약과 합치, 조직 표준 스택과 일치, Level 3 유지
- H2 다국어 1급 — 상위 Seed 재작성 필요(Core Problem 변경 불가), Level 4 재분류
- Decision: 옵션 1
- Rationale: 권장안 Q1 수용. 비-Dart 파일 편집은 상위 C-03 후단이 이미 허용하므로 풀스택 워크스페이스의 SQL · YAML · Dockerfile 편집에 지장이 없다
- Impact: Seed C-02 · AC-11 · A-01
- Source: Discovery §5.1 Q1 권장안 → 오너 "계속 진행"
D-002: cocode ADE 의 하위 모듈로 app/cocode 안에 구축
- Date: 2026-09-16
- Context: 별도 앱을 만들면 셸 · 패키지가 중복되고 Level 4 가 된다. 상위 cocode/AC-03 이 요구하는 표면이 바로 이 작업이다
- Options: 1. 하위 모듈 2. 별도 앱
- Decision: 옵션 1
- Rationale: 권장안 Q2 수용. Discovery §1.3 — 패키지는 있고 셸만 비어 있다
- Impact: Seed C-01 · AC-01 · A-02. Level 3 유지
- Source: Discovery §5.1 Q2 권장안 → 오너 "계속 진행"
D-003: 첫 릴리스 범위 = 셸 통합 + 에디터 코어 + 실행 구성 · PTY 터미널
- Date: 2026-09-16
- Context: Discovery §4.2 의 네 트랙(A 셸 통합 · B 에디터 코어 · C 풀스택 표면 · D 보조 사이드바) 중 어디까지를 첫 릴리스로 볼지
- Options: 1. A+B 만 2. A+B+C(실행 구성 · PTY)까지 3. A~D 전부
- Decision: 옵션 2. DB 브라우저 · API 클라이언트 · DAP 디버거 · git · pub.dev · 분할 뷰 · 세션 복원 · 대체 화면은 May(AC-14~AC-21)로 두고 Discovery §1.5 도그푸딩 실험 ⓑ 결과로 순서를 정한다
- Rationale: 권장안 Q3 수용. 옵션 3 은 Discovery §5.2 위험 5(범위 팽창 → Level 4)
- Impact: Seed §4 Must 10 · Must-Not 3 · May 8
- Source: Discovery §5.1 Q3 권장안 → 오너 "계속 진행"
D-004: 에디터 코어는 re_editor 0.10.0 유지
- Date: 2026-09-16
- Context: flutter_ide 는 Monaco(WebView)를 쓴다. 최신
flutter_monaco3.4.3 도 Linux 미지원 · 에셋 약 30MB(pub.dev, 2026-09-16) - Options: 1.
re_editor유지(상위 D-066) 2. Monaco 재검토 3. 자체 텍스트 레이어 - Decision: 옵션 1. 옵션 3 은 상위 D-066 의 유보를 그대로 둔다
- Rationale: 권장안 Q4 수용. 상위 C-01 의 데스크톱 3종 앞에서 Monaco 는 탈락. 다중 탭 조건의 응답성은 미검증(A-03)
- Impact: Seed A-03 · AC-03~AC-07
- Source: Discovery §5.1 Q4 권장안 → 오너 "계속 진행"
D-005: 별도 Seed Spec 을 쓰되 상위 불변 제약을 상속
- Date: 2026-09-16
- Context: 상위 Seed(
docs/seed-spec-cocode.mdv3.4.0)는 DRAFT 라 evolve 프로토콜 대상이 아니고, 범위가 다르다 - Options: 1. 별도 Seed + 상속 조항 2. 상위 Seed evolve
- Decision: 옵션 1
- Rationale: 권장안 Q5 수용
- Impact: Seed C-01. 번호 공간 분리 규칙(Metadata)
- Source: Discovery §5.1 Q5 권장안 → 오너 "계속 진행"
D-006: Serverpod 4 serverpod start 는 명령 템플릿으로 시작, stable 후 1급 승격
- Date: 2026-09-16
- Context: 4.0 은 2026-07-08 public beta. Discovery 차별화 1 은 이 명령을 1급 실행 구성으로 둔다고 했다(Socratic L3 C-03)
- Options: 1. 명령 템플릿(설정 값)으로 시작 2. 1급 실행 구성으로 즉시 구현
- Decision: 옵션 1
- Rationale: 권장안 Q6 수용. beta 명령 변경 위험을 코드가 아니라 설정이 흡수한다
- Impact: Seed AC-08 · A-05. 템플릿 스키마는 Design 결정
- Source: Discovery §5.1 Q6 권장안 → 오너 "계속 진행"
D-007: 도그푸딩 · 벤치 워크스페이스 = unibook (상위 D-019 의 SHA 고정)
- Date: 2026-09-16
- Decision: 권장안 Q7 수용
- Impact: Seed A-08 · Discovery §1.5 실험
- Source: Discovery §5.1 Q7 권장안 → 오너 "계속 진행"
D-008: 터미널 PTY 는 flutter_pty2 1.0.2 평가를 허용
- Date: 2026-09-16
- Context: 상위 D-067 은 xterm2 뷰의 IME 결함 재현으로 xterm2/flutter_pty2 를 미채택했다. PTY(프로세스 층)와 터미널 뷰는 분리 가능하다
- Options: 1.
flutter_pty2평가 후 채택 여부 결정 2. 자체 FFI PTY 구현 - Decision: 옵션 1
- Rationale: 권장안 Q8 수용. 뷰는
terminal자체 구현을 유지하고 백엔드만 교체한다(CocodeTerminalBackend계약) - Impact: Seed A-04 · AC-09
- Source: Discovery §5.1 Q8 권장안 → 오너 "계속 진행"
D-009: 편집 리스가 걸린 파일의 사람 편집 — 저장은 막지 않고 표시하며, 에이전트 쪽이 해시 불일치 충돌을 진다
- Date: 2026-09-16
- Context: 상위 cocode/AC-12(같은 파일 동시 편집 방지)는 두 DelegatedTask 사이의 규칙이고 사람은 대상이 아니다. 사람이 리스가 걸린 파일을 편집할 때의 규칙이 상위 문서에 없다(Socratic L3 C-02)
- Options:
- 사람의 저장을 차단 — 사람이 에이전트에 종속됨, ADE 원칙과 충돌
- 저장을 막지 않고 리스 상태를 표시 — 에이전트의 다음 편집은 상위 AC-02 의 result_content_hash 불일치로 충돌 처리(기존 메커니즘 재사용)
- 사람 편집도 리스를 획득 — 상위 C-02 후단(사람 편집은 대상 아님)과 충돌
- Decision: 옵션 2
- Rationale: 새 규칙 없이 기존 충돌 메커니즘으로 닫힌다. 진행자 판단 — 오너 재확인 대상
- Impact: Seed C-03 · AC-10
- Source: Socratic Interview Level 3 C-02
D-010: 사람 편집은 EditSnapshot 을 만들지 않는다 (상위 C-02 후단 승계)
- Date: 2026-09-16
- Decision: 상위 cocode/C-02 의 문면을 그대로 따른다. 전역 검색 치환도 사람 편집이다
- Impact: Seed C-03 · AC-06 · AC-13 · A-07
- Source: 상위 Seed Spec 문면 확인(Socratic L2 A-07)
D-011: 앱 자신의 명령은 사용자 터미널의 표준 입력에 쓰지 않는다
- Date: 2026-09-16
- Context: flutter_ide 는 브랜치 체크아웃 ·
flutter run·flutter pub add를 터미널 PTY 에 문자열로 타이핑한다(git_sidebar.dart:880,883,editor_screen.dart:894). 브랜치명 셸 인젝션과 포그라운드 프로세스 오입력이 실재한다(Discovery §4.1) - Options: 1. 같은 패턴 허용 2. 단일 런처 전용
- Decision: 옵션 2
- Rationale: 상위 DD-15 와
check_process_launch_allowlist.py가 이미 같은 방향을 강제한다 - Impact: Seed C-05 · AC-12 · TerminalSession 불변식 1
- Source: Discovery §4.1 실사
D-012: 이 명세가 정하지 않는 값 3종의 이관
- Date: 2026-09-16
- Decision: (1) DB 접속 비밀의 저장 위치와
CredentialVault키 네임스페이스 → Design (2) Dart 이외 언어 서버(SQL · YAML)의 설치 · 기동 정책 → Planning/Design (3) 응답성 · IME 목표치 → 상위 D-065/D-069 절차. Seed §4 의 어떤 항목도 이 값에 의존하지 않는다 - Source: Socratic Interview Level 5
When adding a new decision, copy the template above and increment the number.
D-013: 롤백 사전검사에 "체인 불연속" 충돌 조건을 추가한다 (CH-001) — 상위 파이프라인 결정 필요
- Date: 2026-09-16
- Context: v1.0.0 C-03 은 "에이전트의 다음 편집은 상위 AC-02 의 해시 불일치 규칙으로 충돌 처리된다"고 했으나, 상위 AC-02/D-036 의 사전검사는 롤백 대상 작업의 마지막 스냅샷 result 해시만 대조한다. 작업 T 의 S1 → 사람 편집 → T 의 S2(base=사람 편집본) 뒤 T 를 시작 시점으로 롤백하면 현재 해시 == S2.result 라 충돌 없이 통과하고 사람 편집이 고지 없이 소멸한다(Contrarian CH-001, Critical)
- Options:
- 사람 편집도 EditSnapshot 을 남긴다 — 상위 C-02 후단과 충돌, C-03 폐기
- 롤백 사전검사에 조건을 하나 더한다 — 롤백 구간 안에 체인 불연속(S2.base ≠ S1.result)이 있으면 충돌로 제시. 상위 AC-02 의 강화(충돌을 더 보고)이며 완화가 아니다
- 규칙을 두지 않고 위험을 인수 — flutter_ide 를 "구조적 데이터 손실"로 폐기한 문서가 같은 결함을 승인하는 셈
- Decision: 옵션 2. Seed §0 "체인 불연속" · C-03 · AC-22(Must-Not)
- Rationale: 매 에이전트 편집이 스냅샷을 남기고(상위 C-02) 다른 작업의 편집은 상위 AC-02 두 번째 조건(전역 마지막 스냅샷 소유)이 잡으므로, 같은 작업의 연속 스냅샷 사이 불연속은 사람 편집 또는 외부 도구 변경뿐이다 — 판정이 필요 없는 결정적 조건
- Impact: 구현은 상위
workspaceRollbackPrecheck확장 — 상위 파이프라인(cocode ADE)의 결정 사항이므로 오너 재확인 1순위에 D-009 와 함께 올린다. D-009 의 "리스 상태 표시"는 "실행 중 작업의 편집 대상 표시"로 대체된다(D-045 리스 구간은 게이트 호출 한 번뿐이라 사람이 만날 수 없다) - Source: Contrarian Review CH-001
D-014: A-01(Dart 풀스택)은 "오너 일괄 수용으로 잠정 결정"으로 표기하고 Verified 로 적지 않는다 (CH-002)
- Date: 2026-09-16
- Context: 상위 Contrarian CH-001 은 "미해결 입력 위에 세운 제약"을 Critical 로 잡았고 그 스펙은 8라운드 게이트를 넘지 못했다. 이 문서의 H1 확정 근거는 오너의 "계속 진행 해줘" 한 마디다
- Decision: §5 A-01 = Unverified(잠정 결정, 개별 확답 대기). Metadata 에 "잠금 근거" 줄을 두어 근거의 실체와 H2 정정 시 문서 폐기를 명시. 잠금은 이 표기 위에서 진행하고, 평가 보고서 · 최종 보고에 오너 재확인 1순위로 올린다
- Rationale: 결정과 검증을 섞지 않는다(상위 자기 인용 패턴 회피). 비대화형 세션에서 개별 확답을 받을 수 없으므로 정직한 표기가 유일한 선택지
- Impact: 복잡도 +2(미검증 가정). Seed Metadata · §5 A-01
- Source: Contrarian Review CH-002
D-015: 풀스택 워크스페이스 판정 = pubspec.yaml 디렉터리(깊이 무관)의 의존 판정, 둘 이상 (CH-003)
- Date: 2026-09-16
- Context: v1.0.0 정의 "루트 바로 아래"는 unibook(
app/·backend/아래) 을 배제해 도그푸딩 워크스페이스가 정의상 대상 밖이었다 - Decision: 판정 입력은
flutterSDK 의존 +lib/main.dart(Flutter 앱) ·serverpod의존(Serverpod 서버) ·jaspr의존(Jaspr 웹) 세 가지뿐. 통과한 디렉터리를 "프로젝트 디렉터리"라 부르고 RunConfiguration.working_dir 은 그중 하나 - Rationale: 상위 D-052(플랫폼 디렉터리는 루트 바로 아래)와 반대 방향의 실수를 피한다. 서버 여러 개인 모노레포에서는 실행 구성이 프로젝트 디렉터리를 골라 가리키므로 "어느 서버인가"가 실행 구성 단위로 닫힌다
- Impact: Seed §0 · §3 RunConfiguration 불변식 (1) · AC-08 · AC-14
- Source: Contrarian Review CH-003
D-016: Simplifier 제안 처분과 AC 병합 2건
- Date: 2026-09-16
- Decision: S-001 Accept · S-002 Accept · S-003 Accept · S-004 Reject(핵심 가치) · S-005 Defer · S-006 Accept · S-007 Defer · S-008 Accept. 저자 추가로 AC-02(디렉터리 트리)를 AC-01(셸 통합)에, AC-05(외부 변경)를 AC-04(버퍼-디스크 동기화)에 병합 — 각각 한 표면의 한 계약이라 판정 단위가 어긋나지 않는다. 번호는 재사용하지 않는다
- Rationale: Contrarian 이 요구한 추가 항목(AC-22 · AC-23 · A-10 · A-01 잠정 표기)으로 복잡도가 32.5 까지 오르므로, 판정 단위를 해치지 않는 병합으로 ≤ 30 안에 둔다. 예상 29.5, 정본은 Round 2 재측정
- Impact: Seed §4 머리말 · §5 머리말
- Source: Simplifier Review S-001~S-008
D-017: D-012 이관 목록 확장 (CH-004 · CH-006 · 채점 완전성 지적)
- Date: 2026-09-16
- Decision: D-012 의 세 항목에 더해 (4) 진단 수신 계약의 소유 패키지 — 제안: 에디터 측 sink(상위
SemanticTokenSink와 같은 결),lsp는 전달만, 자가 검증 ④ 입력 아님 → Design (5) 기본 실행 구성 템플릿 3종(flutterRun · serverpodStart · dartTest)의 정본 내용과 실행 전제 목록 → Planning (6) 바이너리 · 비 UTF-8 파일 열기 정책(읽기 전용 열기 또는 거부 + 사유 표시) → Design. Seed §4 의 어떤 항목도 이 값에 의존하지 않는다 - Source: Contrarian Review CH-004 · CH-006 · 모호성 채점 Round 1 권고 4(d)
D-003 (v1.1.0 추가 근거, CH-009): 실행 구성이 첫 릴리스 Must 인 이유
- Date: 2026-09-16
- Rationale 추가: 사용자 명령 셸에 명령을 타이핑하면 프로세스의 기원 추적 · 일괄 종료 · 실행 전제 확인이 없다. 실행 구성은 세션 목록에 origin 을 남기고, 한 번의 조작으로 종료되며, 실행 전제(CLI 존재 · 디바이스 지정)를 실행 전에 확인해 미충족 사유를 표시한다 — 셸 타이핑으로는 얻을 수 없는 성질이다(Seed AC-08 Then). ⚠️ v1.1.1 교정: 환경변수 화이트리스트는 상위 architecture DD-23a 에 따라 사람 터미널에도 적용되므로 실행 구성 고유의 가치가 아니다 — v1.1.0 의 "사용자 명령 셸은 전체 환경을 상속한다"는 서술은 오류였고 삭제한다(Round 2 감사 CH-009 WEAK 판정). A-06(DB · API 내장 선호)은 §5 에서 이 결정으로 이관: May 항목 AC-14 · AC-15 의 채택 여부는 도그푸딩 실험(Discovery §1.5 ⓑ) 결과로 판단한다(S-003)
D-018: "기존 로컬 폴더를 워크스페이스로 열기"는 상위 파이프라인의 미결 항목이다 — 이 Seed 는 "열린 뒤"만 규정한다
- Date: 2026-09-16
- Context: 상위 ux-spec §2.5 UX-D-30 이 명시한다 — "「기존 워크스페이스(로컬 폴더) 열기」는 이 판본의 범위가 아니다 — 미정 · 결정 필요. seed-spec §4 인수 경계 어디에도 기존 워크스페이스를 여는 조항이 없다(AC-05 · AC-13 은 생성만 다룬다)." 이 Seed 의 AC-01~AC-10 은 전부 "워크스페이스가 열렸을 때"를 Given 으로 두며, 여는 경로(생성 · 기존 폴더 열기)는 규정하지 않는다. 그런데 D-007(unibook 도그푸딩)과 Discovery §1.5 실험은 스캐폴딩되지 않은 기존 저장소를 여는 것을 전제한다
- Options:
- 이 Seed 에 Must 를 신설해 기존 폴더 열기(스캐폴딩 없이
.cocode/초기화 · applied_bricks 빈 집합)를 규정 — 상위 Workspace 엔티티의 "applied_bricks MVP 원소 1"(상위 PRD FR-105) 과 어긋나는 새 상태를 하위 문서가 만든다 - 상위 파이프라인의 결정으로 이관하고, 이 Seed 는 "열린 뒤"만 규정 — 도그푸딩 실험은 그 결정에 종속됨을 명시
- 이 Seed 에 Must 를 신설해 기존 폴더 열기(스캐폴딩 없이
- Decision: 옵션 2. Seed 본문은 바꾸지 않는다(AC-01~AC-10 의 Given 은 이미 "열렸을 때"). 오너 재확인 항목에 D-013 · D-014 와 함께 올린다
- Rationale: 워크스페이스 생명주기(생성 · 열기 · 닫기)는 cocode ADE 의 도메인이지 에디터 모듈의 도메인이 아니다. 하위 문서가 상위 엔티티의 상태 공간을 늘리면 상위 spec-evaluation 이 경계한 "판정 주체 미정" 결함을 하위에서 재생산한다
- Impact: Planning 입력(
docs/planning-inputs-fullstack-code-editor.md§2)에 "도그푸딩 전제: 기존 폴더 열기 결정" 등재. Seed 복잡도 변화 없음 - Source: 상위 ux-spec §2.5 UX-D-30 실사(2026-09-16)
D-019: Design 리뷰 · 흐름 순열이 요구한 규칙 확정 (Solutioning Gate 전)
- Date: 2026-09-16
- Context: Architect 리뷰(AR-01~21) · UX 리뷰(UR-01~15) · 흐름 순열(127 순열, Critical E-C1~3 · Important 29)이 규칙이 비어 있는 지점을 지목했다. 값이 아니라 규칙이 비어 있었으므로 Seed 를 뒤집지 않는 범위에서 Design 이 정한다
- Decision:
- PTY 기동 =
ProcessLauncher.startPty단일 메서드 +core.PtySpawner주입(ED-06) — Seed C-05 "단일 런처" 해석을 "메서드 하나 + 주입 구현 1파일"로 명시(architecture §11-1) - 종료 · 닫기 절차 6단계 · 고아 프로세스 0 · 일괄 저장 부분 실패 시 진행 중단(ED-16 · UX-E-18/19) — E-C1 · E-C2
- running 세션
×비활성, 종료는 K4 버튼(UX-E-20) — E-C3 - 유즈케이스 6종 + Bloc optional constructor injection(ED-03) · 워크스페이스 스코프 컨테이너(ED-13) ·
terminate({grace})(ED-15) ·DiagnosticsSink13번째 계약(ED-09) ·.cocode/editor/소유(ED-07) · Windows 셸 순서(ED-11, U-13 닫힘) · 제3 프로파일 범주(ED-12) - UX 규칙 36건(UX-E-01~36) — 활성 탭 MRU · 진단 표시 범위 · 다시 실행 = 새 세션 · 중복 실행 허용 · 배너 다중 작업 · 디바이스 선택 · JSON 손상 처리 · Semantics 이중화 등
- PTY 기동 =
- Impact: PRD v1.1 부연(FR-206 · 302 · 406 · 501 · 505 · 606) · BDD 시나리오 29건 추가(54 → 83: 흐름 순열 Must-Have 12 · Should-Have · rev.1 재판정 R-0 필수 · R-2 · R-5 · R-9) · architecture rev.4(2라운드 R2-01~06: L0/L2 경로 키 · 해시
String(ED-18) · 포트 분류(ED-19) ·PtyHandle.terminate· store write-through ·PtyLaunchRequest합성) · ux-spec rev.1a(UR-16: 저장 실패 토스트 →Banner(.destructive), UX-E-37). Seed §4 문면 변경 0 - Source: scratchpad/design-review-architect.md · design-review-ux.md · docs/flow-permutation-fullstack-code-editor.md
D-041: 비-git 무시 matcher 는 직접 만들고, 캐시 무효화는 전량이다 (U-14 확정)
- Date: 2026-09-16
- Context: FR-302 가 세 모드를 규정하면서 두 가지를 Design 으로 남겼다 — (a) git 저장소가 아닐 때 쓰는
.gitignorematcher 를 무엇으로 할 것인가, (b) 판정 캐시를 언제 무효화할 것인가. #335 가 그 둘을 정한다. - Options (a):
- pub 의 gitignore matcher 패키지를 의존한다
- 필요한 부분집합만 순수 Dart 로 직접 만든다
- Decision (a): 옵션 2 — 직접 만든다(
gitignore_rules.dart). - Rationale (a): 이 매처가 도는 것은 후퇴로다. git 저장소에서는
git check-ignore가 정본이고(ED-06a), 이 경로가 필요한 것은 git 저장소가 아니거나 git 을 쓸 수 없을 때다. 그 두 경우에 필요한 것은 루트.gitignore하나이며 하위 디렉터리 · 전역 설정 ·.git/info/exclude는 애초에 대상이 아니다(#335 범위). 그 좁은 몫을 위해 L1 에 의존을 하나 더 들이면, 그 패키지의 해석이 git 과 갈리는 날 어느 쪽이 맞는지 판정할 근거가 우리에게 없다. 직접 만들면 지원 범위를 우리가 적을 수 있고, 그 목록이 소스 주석과 테스트에 그대로 있다.- 지원: 주석 · 빈 줄 · 부정(
!) · 디렉터리 한정(build/) · 루트 고정(/x) ·*?[abc]·** - 미지원: 하위
.gitignore· 전역 설정 ·.git/info/exclude· 이스케이프(\#) · 후행 공백 세부. 그 차이가 문제가 되는 상황은 git 이 있는 경우이고, 그때는 이 매처가 아니라git check-ignore가 답한다.
- 지원: 주석 · 빈 줄 · 부정(
- Options (b): 1. 바뀐 규칙이 영향을 주는 디렉터리만 무효화 2. 전량 무효화
- Decision (b): 옵션 2 — 전량. 캐시 단위는 디렉터리이고,
.gitignore변경 알림(FileChangeWatcher ⓑ · #331)이 오면 캐시를 모두 비우고 루트 규칙을 다시 읽는다. - Rationale (b): 어느 디렉터리가 영향을 받는지 계산하려면 규칙을 해석해야 한다. 그 해석이 정확하다면 애초에 git 에 물어볼 이유가 없다 — 우리가 직접 판정하면 된다는 뜻이고, 그것은 이 설계가 거부한 전제다. 캐시 한 벌을 다시 채우는 비용(디렉터리당
check-ignore1회, 펼쳐진 것만)이 그 계산의 위험보다 싸다. - Impact: PRD U-14 행 → 확정.
IgnoreMatcher3모드 + 디렉터리 단위 캐시 +invalidate(). 검색(FR-304)이 같은 인스턴스를 재사용한다. - Source: #335 구현 ·
package/workspace/lib/src/ignore/
D-042: Diagnostic 은 LSP 필드의 부분집합이고, 고른 기준은 「화면이 그리는 것」이다 (§10-3 확정)
- Date: 2026-09-17
- Context: architecture §10-3 이 「
DiagnosticsSink값 타입 상세(LSPDiagnostic필드 부분집합)」를 EPIC-4 S3(#343)에 위임했다. LSP 3.17 의Diagnostic은 9개 필드를 갖는다. - Options:
- LSP 필드를 전부 가져온다
- 화면이 그리는 것만 가져온다
- 지금 필요한 것만 가져오되 확장 여지를
Map<String, Object?> extra로 남긴다
- Decision: 옵션 2 —
range·severity·message·code?넷. - Rationale:
[문제]패널 행은 「심각도 아이콘 · 파일 · 행:열 · 메시지」이고(ux-spec §4.4) 거터 표식은 그 행 번호를 쓴다. 그 밖의 필드는 그릴 자리가 없다:source— 첫 릴리스의 서버가 하나다(Dart). 「어느 서버가 말했나」를 그릴 칸이 없다tags(unnecessary · deprecated) — 흐리게 그리기 · 취소선이 ux-spec 에 없다relatedInformation— 「여기도 보라」는 이동 UI 가 필요한데 그 화면이 없다codeDescription— 규칙 문서 링크. 외부 브라우저를 여는 경로가 첫 릴리스 밖이다data— code action 용 불투명 값. code action 이 없다 옵션 3(extra)을 버린 이유: 쓰지 않는 값을 들고 다니면 「왜 안 보이지」를 다음 사람이 디버깅한다. 없는 필드를 나중에 더하는 것은 값이지만, 있는데 안 그리는 필드는 부채다.
- 부수 결정 2건:
- 좌표는 0부터(LSP 그대로) — 화면이 1부터 세는 것과 다르다. 여기서 바꾸면 거터 표식 · 커서 이동이 각자 되돌리기를 해야 한다. 변환은 그리는 자리 한 곳에서 한다.
- 모르는 심각도는
info— 던지거나 버리면 「서버는 말했는데 화면에 없는」 진단이 생긴다. LSP 1~4 와 Dart enum index(0부터)를 같은 것으로 다루지 않도록 변환은DiagnosticSeverity.fromLsp한 곳에서만 한다.
- Impact:
core/src/contract/diagnostics_types.dart신설 ·contractTypes12 → 13(ED-09) ·lsp선택 인자diagnostics(기본NullDiagnosticsSink— 기존 47 테스트 회귀 0) ·editorCocodeDiagnosticsSink. 상위 C5(자가 검증 ④ 는 진단을 쓰지 않는다)는toolchain소스 스캔 테스트가 판정한다. - Source: #343 구현 · docs/architecture-fullstack-code-editor.md §4.7 · §10-3 · ux-spec §4.4
D-074: 터미널은 xterm3 뷰 + flutter_pty 다 — AGPL-3.0 을 오너가 수용했다 (D-008 대체 · 상위 D-067 관련)
- Date: 2026-09-27
- Context: D-008 은 PTY 백엔드만 바꾸고 뷰는 자체 구현을 유지하기로 했다. 그러나 자체 파서는 커서 이동 · 대체 화면을 버린다(
cocode_ansi_parser.dart머리말) — 로그인 셸(zsh 줄 편집 · powerlevel10k) · vim ·serverpod startTUI 를 그릴 수 없다. 오너가xterm3·flutter_pty채택을 지시했다. - Options: 1.
xterm36.3.4(AGPL-3.0) 2.xterm4.0.0(MIT) 3.xterm25.2.0(MIT, 상위 D-067 이 IME 결함 재현) - Decision: 옵션 1 — 라이선스를 명시해 묻고 오너가 AGPL 을 수용했다(2026-09-27 세션). PTY spawn 은 ED-06 그대로 —
ProcessLauncher.startPty가 정책을 적용하고FlutterPtySpawner(유일한Pty.start파일)가 spawn 한다. - Rationale: 세 뷰의 API 가 같은 계열이라 되돌림 비용은 import 한 줄이다. 로그인 셸 · TUI 가 실제로 그려지는 것(macOS 실측 — oh-my-zsh 배너 · p10k 프롬프트 · Serverpod TUI)이 IDE 터미널의 최소 조건이다.
- Impact: ⚠️ ADE 배포물 전체가 AGPL-3.0 의무를 진다(배포 · 네트워크 제공 시 전체 소스 공개).
THIRD_PARTY_NOTICES.md갱신 대상. AC-16(한글 IME)은 xterm3 뷰에서 다시 재야 한다 — 기존 재생 벤치마크는 자체 뷰를 몬다.flutter_pty0.4.2 결함(실행 파일 부재 시 크래시 · SIGTERM 무시 · Windows 인자 중복)은 런처의 존재 검사 · SIGHUP 종료로 우회했고, 근본 해소는flutter_pty22.0.0(MIT) 재평가다. - Source: 오너 지시(2026-09-27) · 패키지 조사(pub.dev 라이선스 태그 · 소스 실측)
D-075: 편집 표면은 macOS · Windows 에서 Monaco(flutter_monaco 3.4.3), Linux 는 re_editor 유지 (D-004 부분 대체)
- Date: 2026-09-27
- Context: D-004 는 Monaco 를 Linux 미지원 · 에셋 30MB 로 탈락시켰다. 오너가 Monaco 채택을 지시했다.
- Options: 1. 3-OS 전부 Monaco(Linux 불가) 2. 플랫폼별 표면 — Monaco 가 되는 곳만 Monaco 3.
re_editor유지 - Decision: 옵션 2. 본문의 정본은 여전히
EditorDocument.controller이고 Monaco 는 그것을 양방향으로 비춘다(CocodeMonacoSurface). WebView 는 하나 — 탭마다 모델만 바꾼다. - Rationale: 저장 · dirty · 언어 서버 공급 · 모두 바꾸기가 컨트롤러를 읽으므로, 정본을 옮기지 않으면 그 경로를 하나도 고치지 않는다(사본 셋 문제 — discovery 의 flutter_ide 분석). WebView 1개 · 모델 전환 6~12 ms(실측).
- Impact: 앱 번들 +약 16MB(Monaco 0.55.1 에셋) · 첫 에디터 기동 0.7~1.2 s. WebView 가 키를 가져가므로 단축키는 Monaco 동작으로 등록하고(Cmd/Ctrl+S), WebView 밖 누름에서
releaseNativeFocus()로 키보드를 Flutter 에 돌려준다. WebView 안 한글 IME 는 AC-16 측정 대상 밖(재생 하네스가 Flutter 입력을 몬다) — 새 측정 경로가 필요하다. - Source: 오너 지시(2026-09-27)
D-076: macOS 는 창 안 메뉴바를 두지 않는다 — 셸 메뉴를 시스템 메뉴바에 덧붙인다(표준 Edit 유지)
- Date: 2026-09-27
- Context: 창 안
Menubar(File · Edit · View · Delegate · Run · Help)가 시스템 메뉴바와 두 벌이었다(오너 지적). 단, 셸 명령에는 단축키가 없어(U-11) 메뉴가 유일한 진입점이었다. - Options: 1. 창 안 메뉴바만 제거 2. Flutter
PlatformMenuBar로 교체 3. 러너(Swift) 다리로 덧붙이기 - Decision: 옵션 3 —
ShellMenuBridge(AppDelegate.swift) +native_shell_menu.dart. Windows · Linux 는 전역 메뉴바가 없어 창 안 메뉴바가 그대로다. - Rationale: 1은 저장 · 실행에 닿는 길을 없앤다. 2는 메뉴바 전체를 갈아 끼워 표준 Edit(
copy:·paste:·undo:)이 사라지고, WebView(Monaco)의 Cmd+C/V 가 죽는다(WebKit 은 그 키를 메뉴 동작으로 받는다). - Impact: 단축키는 관례값을 임시로 붙였다(⌘O · ⌘S · ⌥⌘S · ⌘W · ⌘R · ⇧⌘R · ⌘. · ⌃` · ⌃⇧`) — U-11 확정 시 교체. 빈 메뉴(Delegate · Help 항목)는 싣지 않는다.
- Source: 오너 지적(2026-09-27)
D-077: 풀스택 실행은 대상의 정본 러너를 PTY 탭에서 띄운다 — 앱은 띄우기 · 끝내기만 한다
- Date: 2026-09-27
- Context: 실행 구성(EPIC-5)은 미구현이고,
runner가 전제한cob dev는 설치된 cob 0.4.0 에 없다. - Options: 1.
flutter run --machineJSON 프로토콜을 앱이 직접 해석 2. 정본 러너를 PTY 탭에서 — Serverpod 서버serverpod start, Flutter 앱flutter run -d <데스크톱> [--flavor], Dartdart run3.cob dev대기 - Decision: 옵션 2. 대상은 워크스페이스의
pubspec.yaml에서 찾는다(run_targets.dart, 깊이 3)..fvmrc가 있으면fvm을 앞에 붙인다. - Rationale:
serverpod start는 코드 생성 · 서버 watch 핫 리로드 · 동반 앱을 한 번에 띄우는 Serverpod 4 의 풀스택 러너이고, 그 TUI 가 xterm3 에서 그대로 동작한다(실측). 핫 리로드 키는 사용자가 누른다 — 앱은 사용자 셸 stdin 에 명령을 타이핑하지 않는다(C-05 · D-011). - Impact: IDE 버튼으로 핫 리로드를 부르는 통합(옵션 1)은 후속. 영속 실행 구성(ED-07)은 여전히 EPIC-5 소관. Dock 에서 뜬 앱을 위해 런처가
$SHELL -l -i로 PATH 를 한 번 읽는다(adoptLoginShellPath). - Source: 오너 지시(2026-09-27) · Lumide
lumide_flutter· roxum-ide 조사
D-078: D-013 옵션 2 를 상위에서 승인했다 — 체인 불연속을 롤백 충돌로 제시한다 (#373)
- Date: 2026-09-28
- Context: D-013 은 옵션 2 를 채택하되 상위 파이프라인(cocode ADE)의 결정이 필요하다고 남겼고, 결정 이슈 #373 은 12일 동안 답이 없었다. 조사해 보니 구현 지점은
workspace가 아니라core의 순수 함수RollbackTargets.compute(rollback_targets.dart:549)였다. 사전검사(PrecheckRollbackUseCase)와 적용 엔진(RollbackEngine)이 같은 결과를 쓴다 - Options: 1. 승인 2. 기각 — AC-22 · C-03 개정, BDD [AC-22] 2건 삭제 3. 조건부 — 에이전트 루프 배선 뒤로
- Decision: 승인. 상위 기록은
cocode/D-074 - Rationale: 충돌을 더 보고하는 방향이라 상위 AC-02 의 강화다. 사람 편집이 조용히 사라지는 경로(CH-001 · Critical)를 남길 이유가 없다. 변경은 순수 함수 한 곳이다. 앱이 아직 에이전트 편집을 하지 않아 서두를 필요는 없지만, 결정을 미룰 이유도 없다. 조건부는 원래 착지점(상위 Epic #21)이 닫혀 있어 새 착지점을 정하는 일만 늘린다
- Impact: #372 차단이 풀린다. BDD [AC-22] 2건의
@pending-d013은 #372 가 푼다. D-013 Impact 의 「workspaceRollbackPrecheck확장」은 「coreRollbackTargets.compute에 조건 추가」로 읽는다 - Source: 오너가 판단을 위임(2026-09-28 — 「모두 직접 판단해서 처리」) · #373 개선 패스 현황 절
D-079: D-018 해소 — 「폴더 열기…」를 기존 폴더 열기의 정식 경로로 승격하고, .cocode/ 는 첫 위임 때 만든다 (#375, 옵션 (a′))
- Date: 2026-09-28
- Context: #437(2026-09-27)이 「폴더 열기…」 · 마지막 폴더 기억 · 「워크스페이스 닫기」를 「D-018 전 임시 경로」로 넣었다. 09-27 부터 앱은 마지막 폴더로 바로 뜬다. 이 경로는
.cocode/를 만들지 않는다. 반대로 상위 UX-D-30 이 정한 생성 진입점(「새 워크스페이스…」)은 앱에 없다. 워크스페이스 스코프 계약 3종(SnapshotStore·WorkspaceFileGate·EditLockRegistry)은 이 결정을 사유로 미등록이었다 - Options: (a) 승격 + 열 때
.cocode/초기화 (a′) 승격 + 첫 위임 때 초기화 (b) 첫 릴리스는 생성 워크스페이스만 (c) 기타 - Decision: (a′).
- 「폴더 열기…」 · 마지막 폴더 기억 · 「워크스페이스 닫기」가 정식 경로다. 최근 목록 · CLI 인자는 #448 이 채운다.
- 여는 것만으로는
.cocode/를 만들지 않는다. 편집 · 검색 · 실행 · 터미널은.cocode/없이 동작한다. - 첫 위임 직전에 확인을 받고
.cocode/를 만들며applied_bricks = []로 둔다. 스코프 계약 3종은 그 시점에 등록한다 — 에이전트 루프 앱 배선(#447) 소관이다. - 상위 Workspace
applied_bricks의 「MVP 원소 1」(상위 PRD FR-105 ·cocode/D-037)은 「생성 워크스페이스 1 · 위임한 기존 폴더 0」으로 개정한다. 개정은 #447 의 Seed 에서 한다
- Rationale: 1차 고객은 외부 Dart/Flutter 개발자(
cocode/D-005)이고, 그들의 첫 동선은 기존 저장소를 여는 것이다. (b) 는 D-007 도그푸딩과 정면 충돌한다. (a) 는 여는 것만으로 사용자 저장소에.cocode/를 남겨 gitignore 안내가 필요해진다. (a′) 는 지금 동작 그대로이고 상위 개정 범위가 가장 좁다 — 위임한 기존 폴더에만 새 상태가 생긴다 - Impact: 「D-018 전 임시」 표기 3곳을 정식 표기로 고쳤다(
workspace_history.dart·commands.dart·app/cocode/pubspec.yaml).bootstrap.dartpendingContracts의 스코프 3종 사유는 #447 을 가리킨다. Patrol 픽스처(스캐폴딩되지 않은 임시 폴더)는 그대로 유효하다. 상위 기록은cocode/D-075 - Source: 오너 위임(2026-09-28) · #375 개선 패스 현황 절
D-080: A-01 확정 — 「풀스택」= Dart 풀스택(H1). 1급 프로젝트 타입의 정본은 C-02 3종이다 (#377, D-014 종결)
- Date: 2026-09-28
- Context: D-014 는 A-01 을 「오너 일괄 수용으로 잠정 결정」으로 두고 개별 확답을 기다렸다. 그 사이 #399 · #433 · #437 이 H1 위에 머지됐고, 09-27 오너 지시(D-074~D-077)도 전부 H1 을 전제했다. 1급 열거는 세 곳에서 달랐다.
- Seed C-02: Flutter 앱 · Serverpod 서버 · Jaspr 웹
- 상위
CocodeProjectType: Dart 패키지 · Flutter 앱 · Flutter 패키지 - D-077
RunTargetKind: Serverpod · Flutter · Dart
- Options: 1. H1 확정 2. H2(다국어) 정정 — 이 Seed 폐기 · Level 4 재분류 · 상위 Seed 개정 선행
- Decision: H1 확정. §5 A-01 = Verified.
- 1급 프로젝트 타입의 정본은 C-02 3종이다.
RunTargetKind(Dart 실행 파일 포함)와CocodeProjectType(Dart · Flutter 패키지 포함)은 「Dart 이외 없음」(AC-11)을 지키는 변형으로 인정한다.- Jaspr 웹은 판정 · 실행 대상이 없다. 이 공백은 EPIC-5(#311) 재착지 때
ProjectDirectoryDetector와 실행 대상에 더해 메운다
- Rationale: 상위 AC-05 · C-03 과
cocode/D-002(Dart/Flutter 깊이 포지셔닝)에 맞는다. H2 는 상위 Seed 개정이 먼저라 이 트리에서 고를 수 있는 선택지가 아니다. 머지된 코드와 오너 지시가 모두 H1 이다 - Impact: #371 의 AC-11 판정 문장은 「Dart 이외의 1급 항목 없음」이 기준이다. Seed §5 A-01 문면과 Metadata 「잠금 근거」 갱신은 Seed 진화(#443)에서 한다
- Source: 오너 위임(2026-09-28) · #377 개선 패스 현황 절
D-081: 에이전트 루프의 앱 배선은 새 Project(#447) 로 세우고 #305 잔여와 병행한다 (#446)
- Date: 2026-09-28
- Context: 앱은 계약 13종 중 5종만 등록한다.
AgentRuntime·VerificationRunner·SandboxPolicy· 스코프 3종 ·FileFormatParser는 「상위 결정」「상위 Epic 소관」을 사유로 미등록인데, 그 사유가 가리키던 상위(#2 · #21 · #33 · #43 · #62 · #79)는 전부 닫혔다. 이 Seed 는 이 배선을 범위 밖에 둔다 - Options: (1) 새 Project (2) #305 에 EPIC-8 추가 — Seed 개정 · Level 재평가 (3) #305 출하 뒤로 미룸
- Decision: (1).
- #305 는 Seed v1.1.1 범위로 닫는다. 새 Project 는 #305 출하를 기다리지 않는다.
- 첫 목표는 수직 슬라이스다. 백엔드 1종으로 위임 → 게이트 · 스냅샷 → 자가 검증 1종 → 승인 · 되돌리기를 앱에서 관통한다.
- 권고(Design 이 바꿀 수 있다): ACP 먼저, 검증 실행기는 정적 분석 먼저.
AgentRuntime등록 모양은 그 Project 의 Design 이 정한다
- Rationale: (2) 는 D-003 이 「범위 팽창 → Level 4」로 기각한 축과 같다. (3) 은 ADE 의 차별점(위임 루프) 없이 IDE 표면만 출하하는 것이고, 상위 Must AC-01 · 02 · 06 · 07 · 08 · 09 를 앱에서 미충족으로 남긴다. 새 Project 는 파이프라인으로 범위와 AC 를 새로 잡을 수 있다. 지금은 구현 처리량보다 결정이 병목이라 병행 비용이 작다
- Impact:
pendingContracts사유가 #447 을 가리킨다. D-079 의.cocode/초기화 · 상위applied_bricks개정 · 생성 마법사 배선이 #447 로 모인다. #372 · #193 · #314 배너의 앱 경로도 #447 로 모인다. 상위 기록은cocode/D-076 - Source: 오너 위임(2026-09-28) · #446
D-082: xterm3(AGPL-3.0)을 담은 빌드는 법무 회신 전까지 외부에 배포하지 않는다 (D-074 후속, #440)
- Date: 2026-09-28
- Context: D-074 로 오너가 AGPL-3.0 을 수용했다. 그런데
docs/oss-notices-cocode.md§4.4 는 번들에 포함하면 법무 검토 요청의 발신 사실과 회신을 결정에 링크하라고 요구하고, 그 기록은 0건이다. AGPL 은 배포물 전체에 대응 소스 제공 의무를 걸 수 있다 - Decision: 요청안(질문 · 배포 형태 · 대안)을 oss-notices §2.1.1 에 준비했다. 발신은 오너가 지정할 법무 창구가 한다. 회신 전에는 xterm3 를 담은 빌드를 외부에 배포하지 않는다 — 내부 빌드와 도그푸딩만 한다. 회신이 부정적이면 MIT
xterm4.0.0 으로 되돌린다(import 한 줄 — D-074 Rationale) - Rationale: 오너의 수용(D-074)은 유지한다. 다만 수용과 법무 판단은 다른 일이다. 되돌림 비용이 작은 동안 배포를 막아 두면 판단이 늦어져도 잃는 것이 없다. 지금은 릴리스 브랜치도 태그도 없어 이 제약의 비용은 0 이다
- Impact: 첫 외부 릴리스의 전제 조건에 #440 회신이 붙는다
- Source: 오너 위임(2026-09-28) · #440 · oss-notices §4.4
D-083: 워크스페이스 검색이 에디터 찾기(Monaco)와 같은 토글을 준다 — [ab] 단어 단위 · [AB] 대소문자 보존 · 제외 glob (ux-spec §4.3 「첫 릴리스 밖」 해제)
- Date: 2026-09-28
- Context: D-075 로 편집 표면이 Monaco 가 되면서 에디터 안 찾기 위젯은
[Aa][ab][.*]와 치환의[AB]를 준다. 워크스페이스 검색(S-E03)은[.*][Aa]만 있었다(ux-spec §4.3 「단어 단위 · 글로브는 첫 릴리스 밖」). 같은 검색어 · 같은 토글이 두 곳에서 다른 자리를 맞히면 사용자는 어느 쪽이 맞는지 판정할 수 없다. 오너가 Monaco 찾기/바꾸기를 워크스페이스 검색에 반영하고 제외 형식을 더하라고 지시했다. - Options: 1. 토글만 더하고 판정은 Dart
\b2. Monaco 판정 이식 —wordSeparators경계 · VS CodebuildReplaceStringWithCasePreserved3. 워크스페이스 검색 대신 에디터 찾기만 쓴다 - Decision: 옵션 2. 정규식 조립을
coresearchPatternOf한 함수로 모아 검색 isolate 와 치환이 함께 쓴다(이전에는 두 벌을 같게 유지했다). 대소문자 보존은preserveCaseOf. 제외는 쉼표로 가른 glob 이고.gitignore한 줄 문법이다 —GitignoreRule을 재사용하며, 조상 디렉터리가 제외되면 그 아래를!로 되살리지 않는다(git 규칙). 해석되지 않는 glob 은 스트림을 열기 전에SearchExcludeException으로 던진다. - Rationale: Dart
\b는 ASCII 단어 글자만 알아 한글 옆 경계가 Monaco 와 갈린다(id값에서\bid\b는 맞고 Monaco 는 안 맞는다). 맞은 첫 · 끝 글자가 구분자인 경우(.foo)도 판정이 다르다. glob 을 새 문법으로 만들면 트리의 무시 규칙(#335)과 같은 글이 다른 뜻이 된다. 제외 오류를FormatException으로 올리지 않는 것은 정규식 오류와 고칠 입력칸이 달라서다. - Impact:
WorkspaceSearchPort.search·SearchWorkspaceUseCase에wholeWord·excludes인자(기본값 — 호출부 호환).SearchSummary.excludedFileCount→ 요약 «N개 파일 제외».[검색어 지우기]가 토글 · 제외 glob 을 남긴다(입력칸과 상태가 갈리지 않게). 로컬 아이콘 묶음에case-upper추가. 남는 차이: 치환의 정규식 캡처 참조($1)는 여전히 글자 그대로다(ux-spec §4.3 결정) · 포함 glob · 결과 행 단위 바꾸기는 범위 밖. - Source: 오너 지시(2026-09-28) · Monaco
USUAL_WORD_SEPARATORS· VS Codesrc/vs/base/common/search.ts
D-084: 편집 그룹(화면 분할)은 세션 상태의 배치다 — 탭 끌어 놓기로 옮기고 나누며, 그룹마다 Monaco 표면 하나 (D-075 부분 개정 · 창 분리는 후속)
- Date: 2026-09-28
- Context: 오너가 Flutter 3.47 데스크톱 멀티 윈도우로 ① 탭을 끌어 별도 창으로 분리 ② 그 창을 탭 줄에 다시 붙이기 ③ 탭 끌기로 화면 분할을 지시했다. 3.47.3 실측: windowing API 는
package:flutter/src/widgets/_window.dart의@internal실험 기능(공식 지원은 main 채널 +--enable-windowing, 「프로덕션 금지 · 패치 버전에서도 깨짐」). stable 엔진도 FFI 심볼 22개를 싣고 있어--dart-define=FLUTTER_ENABLED_FEATURE_FLAGS=windowing으로 켜지기는 한다. 그러나 ⓐRegularWindowController에 창 위치 get/set · 이동 이벤트가 없고(windowHandle로 네이티브 보강 필요) ⓑ 창 기능을 켜면 러너가 「창 없는 엔진 + Dart 가 창 생성」이 되고 뷰 id 가 1부터라 implicit 뷰가 사라진다 →flutter_monaco의 포커스 전환(registrar.view)이 메인 창에서도 깨지고,ShellMenuBridge(D-076)가mainFlutterWindow에 묶여 있다. - Options: 1. 분할 먼저(stable API) · 창 분리는 개발 빌드 실험 플래그 후속 2. 둘 다 지금 실험 API 로 3. 분할만(창 분리는 stable 공식 지원 대기)
- Decision: 옵션 1(오너 선택). 분할의 모델:
EditorSessionState에layout(EditorLayout— 분할 트리 · 그룹{탭 순서 · 활성 탭} · 활성 그룹) 필드를 둔다.activeTab은 필드가 아니라 활성 그룹에서 읽는 getter 다. 필드 열거 가드(ED-02)를 이 결정으로 갱신했다 — 배치는 경로 키와 그룹 번호뿐이고 열기 · 닫기 · 옮기기 · 떼기에서만 바뀐다.- 한 문서는 한 그룹에만 열린다 — Monaco 모델이 WebView 마다 생기므로, 같은 문서를 두 그룹에 열면 정본 하나에 사본이 둘 붙고 닫기 처분(#328)이 「마지막 사본인가」를 물어야 한다.
- 닫기의 다음 활성은 같은 그룹 안의 MRU다. 비는 그룹은 걷고(마지막 그룹은 남긴다) 트리를 접는다. 그룹 번호는 재사용하지 않는다.
- 그룹마다
CocodeMonacoSurface하나(D-075 의 「WebView 하나」를 「그룹당 하나」로 개정). 그룹 칸은 GlobalKey 로 붙잡는다 —Resizable이 칸을 키 없이 자리 순서로 맞춰, 트리가 바뀌면 다른 그룹의 WebView 가 재사용되거나 다시 뜬다.
- Rationale: 배치를 세션 밖 모델로 두면 「같은 그룹 안 MRU」 닫기 규칙이 두 곳에 생긴다. 분할은 실험 API 가 필요 없어 지금 안전하게 낼 수 있고, 창 분리는 러너 재작성 ·
flutter_monaco포크 · 네이티브 창 위치 보강이 한 덩어리라 따로 판정할 가치가 있다. - Impact: ~~문서를 다른 그룹으로 옮기면 그 문서의 Monaco 실행 취소 이력이 끊긴다~~ → D-085 으로 이어진다. ~~한 문서는 한 그룹에만~~ → D-085 으로 해제. ~~분할선 비율은 상태가 아니라 구조가 바뀌면 같은 비율로 다시 선다~~ → D-085 으로 남은 칸끼리의 비율을 지킨다. 옮기기 직전 150 ms 창의 편집은 모델을 닫기 전에 끌어와 잃지 않는다. WebView 위 누름은 Flutter 로 오지 않아 활성 그룹 판정은 Monaco
onFocusChanged로 한다. 분할선 비율은 상태가 아니라 구조가 바뀌면 같은 비율로 다시 선다. 후속(창 분리): 개발 빌드 한정FLUTTER_ENABLED_FEATURE_FLAGS=windowing· 러너(AppDelegate·MainFlutterWindow) 재작성 ·flutter_monaco포커스의 다중 뷰 대응(포크 또는 상류 PR) ·windowHandle기반 창 위치 · 이동 추적(탭을 끌어 창 밖에 놓으면 그 자리에 창, 창의 탭을 다른 창 탭 줄에 놓으면 합치기). - Source: 오너 지시 · 선택(2026-09-28) · Flutter 3.47.3 SDK 실측(
_features.dart·_window_macos.dart·FlutterEngine.mmaddViewController·FlutterCompositor.mm) ·flutter_monaco3.4.3FlutterMonacoPlugin.swift
D-085: 분할의 남은 제약을 푼다 — 실행 취소 이력 이관 · 같은 문서 여러 그룹 · 분할선 비율 유지 (D-084 개정 · UX-E-13 결함 정정)
- Date: 2026-09-28
- Context: D-084 는 세 제약을 남겼다 — ⓐ 문서를 다른 그룹으로 옮기면 Monaco 실행 취소 이력이 끊긴다(그룹마다 WebView 가 따로) ⓑ 한 문서는 한 그룹에만 ⓒ 분할 구조가 바뀌면 비율이 균등으로 돌아간다. 오너가 모두 풀라고 지시했다. 조사 중 기존 결함도 드러났다:
CocodeMonacoSurface가 버퍼 쪽 변경(모두 바꾸기 · 디스크에서 다시 읽기)을MonacoDocument.setText(=model.setValue, 실행 취소 스택을 비운다)로 비춰, Monaco 에서 모두 바꾸기를 Cmd+Z 로 되돌릴 수 없었다(UX-E-13 위반).applyEdits도model.applyEdits라 실행 취소에 오르지 않는다. - Options (ⓐ): 1. 실행 취소를 버퍼(Dart) 쪽으로 옮기고 Monaco 의 Cmd+Z 를 가로챈다 2. Monaco 이력을 걸어서 꺼내 새 모델에 다시 쌓는다 3. 문서마다 WebView(D-075 가 비용으로 탈락시킨 길)
- Decision:
- ⓐ 옵션 2. 페이지에 cocode 다리(
kCocodeMonacoBridgeScript)를 심는다:exportHistory가 원래 모델에서undo()·redo()를 끝까지 밟아 단계별 본문을 **차이(구간 교체)**로 모으고 제자리로 돌려놓는다(편집기 뷰 상태도 복원).importHistory가 새 모델을 가장 오래된 본문에서 그 차이들을pushEditOperations+pushStackElement로 한 단계씩 다시 쌓는다(다시 실행 단계는 쌓은 뒤 되돌려 복원). 옮기는 쪽이 레이아웃을 바꾸기 전에CocodeMonacoHistoryHandoff.capture로 꺼내 두고, 새 표면이 그 문서의 모델을 열 때 이어 받는다. 앞뒤 각 300단계 상한. - 버퍼 쪽 변경은
applyText— 바뀐 구간만pushEditOperations로 교체한다. 실행 취소가 되고 커서 · 스크롤이 그대로다(UX-E-13 결함 정정, 같은 문서의 다른 그룹 동기화도 이 길). - ⓑ 같은 문서를 여러 그룹에 연다(한 그룹 안에서는 한 번). 트리 · 검색 · 문제의 열기는 활성 그룹에 선다(모두 바꾸기는
preferExisting으로 열린 탭을 쓴다). 닫기는 그룹을 받아 다른 그룹에 남으면 탭만 걷고, 마지막 탭일 때만 처분(#328)을 묻고 버퍼를 놓는다. ⌥ 끌기는 복사,editor.splitRight(⌘) 는 활성 탭을 복제해 오른쪽에 연다. - ⓒ
EditorSplitRatios가 화면 곁에서 줄마다(칸별 첫 그룹 번호 목록) 비율을 들고, 칸이 더해지면 새 칸1/n· 원래 칸은 서로의 비율 유지, 빠지면 남은 칸이 비율대로 나눈다.
- ⓐ 옵션 2. 페이지에 cocode 다리(
- Rationale: 옵션 1 은 Monaco 의 단계 경계를 잃는다 — 150 ms 묶음이 단계가 되어, 쉬지 않고 친 문장이 한 번에 사라지거나 한글 조합 도중 상태가 단계로 남는다. 옵션 2 는 Monaco 자신의 경계를 그대로 옮긴다. 옵션 3 은 탭마다 30~100 MB 다. 같은 문서의 여러 그룹은 VS Code 편집 그룹과 같은 기대이고, 정본이 버퍼 하나라 저장 · dirty · 언어 서버 경로는 그대로다.
- Impact: 이력을 걸어서 꺼내는 동안 모델 이벤트가 단계 수만큼 다리를 건넌다(상한 300 이유). 줄바꿈이 CRLF 인 파일은 모델이 줄바꿈을 자기 것으로 맞춰 구간 교체가 넓어질 수 있다. 같은 문서가 두 그룹에 있으면 한쪽 Cmd+Z 가 다른 쪽에서 비친 편집도 되돌린다(공유 모델과 같은 동작). Linux(
re_editor)는 정본 컨트롤러가 이력을 들고 있어 원래 끊기지 않았다. 창 분리는 여전히 후속(D-084). - 실측에서 함께 잡은 것:
- 분할 회귀(D-084) — 표면마다 「이 표면 밖의 누름이면 키보드를 Flutter 로 돌려준다(
releaseNativeFocus)」를 판정해, 한 편집기를 누르면 다른 표면들이 그 누름을 밖으로 보고 방금 받은 키보드를 빼앗았다. 캐럿은 서는데 타이핑이 안 된다(macOS 실측). 판정을 모든 표면의 밖으로 바꾸고 한 표면만 돌려준다. - 기호 키 단축키 — 네이티브 메뉴(D-076)는 단축키를 글자로 맞춘다. 한글 2벌식에서
\·`키는₩를 내 ⌘\ · ⌃가 메뉴에 닿지 않는다.ShellMenuBridge` 가 기호 키 단축키를 ANSI 물리 키 코드로도 맞춘다(로컬 키 모니터 — WebView 가 키보드를 쥐고 있어도 같은 명령으로 간다). - ⚠️ 이 개발 머신에서는 1Password 기본 단축키(자동 채우기 ⌘) 가 ⌘\ 를 전역으로 가져가 앱에 오지 않는다(같은 경로의 ⌃` 는 온다). 메뉴 · 탭 끌기로는 된다. 단축키를 바꿀지는 오너 결정으로 남긴다.
- 분할 회귀(D-084) — 표면마다 「이 표면 밖의 누름이면 키보드를 Flutter 로 돌려준다(
- Source: 오너 지시(2026-09-28) ·
flutter_monaco3.4.3editor-api.js(document.setText=setValue·document.applyEdits=model.applyEdits) · MonacoITextModel.undo/redo/canUndo/pushEditOperations
D-086: xterm3 를 co-package 포크(6.3.4+cocode.1)로 바꾼다 — 데스크톱 입력기(한글 두벌식) 결함 수정 (D-074 후속, #452)
- Date: 2026-09-28
- Context: 제품 터미널(D-074 · xterm3 6.3.4)에서 두벌식으로 친
한글테스트가ㅎㅏㄴㄱㅡㄹㅌㅔㅅㅡㅌㅡ로 셸에 갔다. SoFluffyOS/lumide#64 와 같은 결함이고, 상위 D-067 이 xterm2 에서 「재현」 판정한 경로다. 원인은 두 겹이고 둘 다 xterm3 안에 있다- ①
terminal_view.dart의 텍스트 폴백이event.character를 바로 쓰고 키를 처리했다고 보고한다. 데스크톱 임베더는 키를 프레임워크에 먼저 주므로 입력기가 키를 보지 못한다 - ②
custom_text_edit.dart가 확정 직후 버퍼를 비운다. 두벌식은 한 키에서 확정과 다음 초성 조합을 함께 하므로, 그 비움이 늘 조합 중에 도착해 macOS 가discardMarkedText로 다음 음절을 끊는다
- ①
- Options: 1.
terminal이 쓰기 세션의 입력기를 소유(xterm3 공개 API 만, 포크 없음) 2. xterm3 를 co-package 에 포크해 결함 위치를 고치고 업스트림에 PR 3. MITxterm4.0.0 으로 되돌림(같은CustomTextEdit계열이라 ②를 그대로 가진다) - Decision: 옵션 2 — 오너 선택(2026-09-28, #452 계획 게이트).
package/terminal은 co-package 태그xterm3-v6.3.4+cocode.1을 git 의존으로 쓴다. 업스트림(klc/xterm3)이 수정을 릴리스하면 pub.dev 로 되돌린다 - Rationale: 결함이 있는 곳을 고친다 — 뷰의 입력 경로를 우리 쪽에 다시 구현하지 않는다. 업스트림 기여로 같은 결함을 가진 다른 사용자도 풀린다. 옵션 1 은 포크가 필요 없어 권장안이었지만 xterm3 의 입력기 층을 그림자 구현으로 대체해야 했다
- Impact:
- AGPL 의무는 그대로다(D-074 · D-082). 포크도 AGPL-3.0-or-later 이고, 번들 경계 가드는 계속 ⚠️ 로 드러낸다
- 고지 생성기는 git 의존을 원칙적으로 판정하지 않는다. 그대로 두면 xterm3 가 강한 copyleft 표와 번들 가드에서 조용히 빠진다. 그래서
IDENTIFIED_GIT_PACKAGES를 신설했다(이 결정 번호로만 행을 더한다). 고지의 출처 링크는 해석된 커밋의 포크 소스 트리 — 대응 소스 위치다 - D-082 로 MIT
xterm4.0.0 으로 되돌아가면 이 수정을 그 패키지에 다시 옮겨야 한다 - AC-16 재측정(#368)은 제품 표면에서 이 포크를 잰다. 옛 재생 하니스는 「키 → 프레임워크 → 입력기」 순서와 엔진의
setEditingState의미를 재지 못한다 — 포크의test/_support/macos_text_input.dart가 그 두 축을 모사한다
- Source: 오너 선택(2026-09-28) · #452 · coco-de/co-package#14