지난 포스팅에서는 Windows·macOS·Linux를 대상으로 한 CI 기준과 Windows 패키징을 점검했다. 로컬 검사가 통과했다는 사실만으로 제품 전체가 검증된 것은 아니므로, 플랫폼별 검사 범위와 실제 실행 가능한 패키지를 분리해 확인하는 과정이었다.
이번에는 Architecture Workspace의 다음 단계에 집중했다. 초기 Architecture Workspace는 새 프로젝트를 시작할 때 arc42, C4 Context·Container·Component, ADR index와 첫 ADR을 한 번에 생성한다. 그러나 첫 문서 세트만 만들어 주면 이후의 결정은 다시 채팅이나 개인 메모로 흩어질 수 있다.
이번 작업의 목표는 다음과 같다.
초기 생성 세트는 다음과 같다.
docs/architecture/arc42.md
docs/architecture/c4-context.md
docs/architecture/c4-container.md
docs/architecture/c4-component.md
docs/adr/README.md
docs/adr/0001-initial-architecture.md
실제 개발에서는 결정이 계속 추가된다. 검색 공급자 대신 격리된 Research View를 선택한 이유, 이미지 원본 저장 방식, document workspace 경계, MVP에서 기능을 제외한 이유처럼 나중에 다시 설명해야 하는 결정이 생긴다.
매번 수동으로 0002 파일을 만들고 README 표에 행을 추가하면 번호 중복이나 index 누락이 생기기 쉽다. 반대로 전체 ADR 세트를 다시 생성하면 사용자가 이미 검토한 기록이 바뀔 수 있다.
따라서 기준을 새 ADR만 추가하고 기존 ADR은 건드리지 않는 것으로 고정했다.

AI Agent가 코드를 분석해 arc42 항목이나 ADR 초안을 제안하는 기능은 나중에 충분히 검토할 수 있다. 다만 ADR은 단순한 텍스트가 아니라 프로젝트의 결정을 승인하는 기록이다.
AI 결과를 곧바로 Accepted로 저장하면 실제 합의가 없는 결정이 공식 문서처럼 남을 수 있고, 코드와 문서의 사실 관계가 틀려도 발견하기 어렵다. 사용자 API key, 외부 LLM 호출, 비용과 개인정보 검토도 추가된다.
그래서 현재 단계에서는 사용자가 제목·상태·배경·결정·결과를 직접 입력한다. AI는 향후 제안 단계에서만 검토하고, 제안 결과도 사용자의 승인 이후 기존 파일 경계와 충돌 검사를 통과하는 별도 흐름으로 두는 방향이다.
명령 팔레트에는 두 개의 아키텍처 관련 진입점이 있다.
초기화는 여러 파일의 일괄 생성과 충돌 확인이 핵심이고, ADR 작성은 한 개의 결정과 index 갱신이 핵심이다. 목적이 다른 작업을 하나의 폼으로 합치지 않아 사용자가 지금 어떤 파일 작업을 요청하는지 분명하게 했다.
ADR 폼은 결정 제목, 상태, 배경, 결정, 결과를 받는다. 상태는 Proposed, Accepted, Rejected, Superseded 중 하나만 허용한다. 빈 document workspace에서 폼을 열 수는 있지만, 선택된 폴더가 없으면 파일 작업은 시작되지 않고 안내 메시지를 보여 준다.
편집 가능한 UI와 파일 저장 권한은 별개의 문제다. 화면이 열렸다는 이유로 임의의 경로에 쓰기를 허용하지 않는 것이 이 기능의 중요한 기준이다.
Electron 보안 경계를 지키기 위해 Renderer에서 파일 시스템을 직접 사용하지 않는다. Renderer는 입력 상태를 관리하고 Preload가 노출한 기능별 API만 호출한다.
const response = await window.dock.architecture.createAdr({
workspaceId,
title: adrTitle,
status: adrStatus,
context: adrContext,
decision: adrDecision,
consequences: adrConsequences,
});
Preload에서는 요청과 응답을 다시 schema로 검증한다.
export const ArchitectureCreateAdrRequestSchema = z
.object({
workspaceId: WorkspaceIdSchema,
title: ArchitectureAdrTextSchema.max(200),
status: ArchitectureAdrStatusSchema,
context: ArchitectureAdrTextSchema,
decision: ArchitectureAdrTextSchema,
consequences: ArchitectureAdrTextSchema,
})
.strict();
제목과 본문에는 길이 제한과 제어 문자 검사가 있다. 파일명은 제목을 그대로 사용하지 않고 줄바꿈과 특수 문자를 정리한 slug로 만든다. README의 Markdown 표에 제목을 넣을 때 표 구분자인 세로 막대도 별도로 escape한다.
Main은 선택된 document workspace의 canonical path를 기준으로 고정된 docs/adr 디렉터리만 사용한다. 그 안에서 NNNN-*.md 패턴을 만족하는 일반 파일의 번호를 읽고 가장 큰 번호에 1을 더한다.
const entries = await fs.readdir(realAdrDirectory, {
withFileTypes: true,
});
const numbers = entries
.filter((entry) => entry.isFile())
.map((entry) => readAdrNumber(entry.name))
.filter((number): number is number => number !== undefined);
const adrNumber = Math.max(0, ...numbers) + 1;
첫 ADR이면 0001, 초기화로 0001이 이미 있으면 0002가 된다. 제목이 ADR 작성 흐름 추가라면 다음과 같은 파일이 생긴다.
docs/adr/0002-adr-작성-흐름-추가.md
한국어 제목도 사용할 수 있으므로 slug를 만든 뒤 파일명의 유니코드 정규화 형태를 유지한다. 파일명과 Markdown 표에 사용되는 문자열은 서로 다른 규칙으로 처리한다.
새 ADR 파일과 README Index는 서로 다른 파일이다. 하나만 성공하면 문서 체계가 어긋난다. 이번 구현은 다음 순서로 처리한다.
새 ADR의 기본 구조는 상태, 배경, 결정, 결과로 고정했다.
# ADR-0002: ADR 작성 흐름 추가
## 상태
Accepted
## 배경
중요한 구조 결정을 채팅에만 남기면 추적하기 어렵습니다.
## 결정
Dock에서 번호가 붙은 ADR을 생성하고 index를 갱신합니다.
## 결과
결정의 배경과 결과를 document workspace 안에서 함께 관리합니다.
기존 ADR 파일은 덮어쓰지 않는다. 새 ADR은 createDocumentWithContent의 exclusive 저장 경계를 사용하고, 기존 index는 내용을 읽어 새 행을 추가한 뒤 writeDocument로 원자적 저장을 요청한다.
이 구조는 다중 Dock 인스턴스가 동시에 같은 번호를 계산하는 경쟁 상태까지 해결하는 완전한 트랜잭션은 아니다. 현재 Dock은 단일 로컬 사용자용 MVP이므로, 이번 범위에서는 기존 파일 보호와 index 실패 시 부분 생성 정리를 우선했다.

초기 README에는 설명과 상태 표가 함께 있다. 새 ADR을 추가할 때 README 전체를 템플릿으로 교체하면 사용자가 직접 추가한 안내나 규칙이 사라질 수 있다.
그래서 기존 문서에서 Index heading을 찾아 해당 섹션에 한 행을 추가한다. heading이 없는 경우에는 기존 내용을 유지한 뒤 최소한의 표를 새로 붙인다.
| [0001-initial-architecture.md](./0001-initial-architecture.md) | Accepted | Dock 초기 아키텍처 문서 세트 |
| [0002-adr-creation-workflow.md](./0002-adr-creation-workflow.md) | Accepted | 애플리케이션에서 ADR 작성 흐름 제공 |
사용자가 표의 열 구조를 완전히 바꾼 경우까지 의미적으로 해석하지는 않는다. 그 수준의 자동 문서 마이그레이션은 현재 단순 생성 기능의 범위를 넘어선다.
이번 작업에서는 한 종류의 테스트만으로 완료를 판단하지 않았다.
Main service는 빈 docs/adr에서 0001 생성, 두 번째 요청에서 0002 생성, 기존 ADR 보존, Markdown 필드 생성, index 누적을 확인한다.
IPC와 Preload는 잘못된 workspace ID, 선택되지 않은 document workspace, 허용되지 않은 상태, 제목 길이 초과, 고정된 architecture:create-adr 채널을 검증한다.
Renderer는 명령 팔레트 진입, 필수 입력, workspace 미선택 안내, 성공 후 Editor 전환을 검증한다. 실제 Electron E2E는 임시 document workspace에서 다음 흐름을 확인한다.
폴더 선택
→ 명령 팔레트
→ ADR 작성
→ 0001 생성
→ Editor에서 열림
→ 다시 ADR 작성
→ 0002 생성
→ README Index에 두 행 존재
최종 검증 결과는 다음과 같다.
| 검증 | 결과 |
|---|---|
| npm run check | 101 tests 통과 |
| ADR 단독 Electron E2E | 통과 |
| 전체 Electron E2E | 14 scenarios 통과 |
| npm run package | 통과 |
| npm run test:smoke | 통과 |
이번 작업으로 ADR은 사용자가 직접 작성하는 로컬 Markdown 기록이 됐다. 번호와 파일명은 Main에서 생성하고, 저장 위치는 선택된 document workspace의 docs/adr로 제한한다. 새 ADR과 README Index를 함께 갱신하며 기존 기록은 덮어쓰지 않는다.
아직 구현하지 않은 항목도 명확하다.
AI를 영원히 배제했다는 뜻은 아니다. 사람이 읽고 고치고 승인하는 문서 흐름을 먼저 고정해야 이후 AI 제안 기능도 같은 저장 경계와 승인 규칙 안에 넣을 수 있다.
Architecture Workspace의 가치는 문서 파일을 한 번 만들어 주는 데서 끝나지 않는다. 프로젝트가 진행될수록 결정이 쌓이고, 그 결정이 현재 구조와 왜 연결되는지 추적할 수 있어야 한다.
이번 ADR 작성 흐름은 작지만 중요한 기반이다. Renderer는 입력과 상태만 담당하고, Preload는 좁은 계약만 노출하며, Main은 번호·경로·충돌·저장을 책임진다. 이 경계를 유지하면 다음 단계에서 arc42 항목 보완이나 C4 문서 제안 기능을 추가하더라도 기존 로컬 파일 보호 규칙을 흔들지 않을 수 있다.
다음 검토 대상은 생성된 문서 세트를 실제 프로젝트에 맞게 편집하는 경험이다. AI를 붙이기 전에 사람이 읽고 고치고 승인하는 문서 흐름이 충분히 자연스러운지부터 확인하는 순서다.