<!-- task=m-client-integration-standardization/02+01_client_bootstrap_skeleton plan=0 tag=CIS-BOOT -->

# Plan - CIS-BOOT

## 이 파일을 읽는 구현 에이전트에게

`CODE_REVIEW-cloud-G06.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수다. 구현 후 검증 명령을 실행하고 실제 stdout/stderr를 붙여 넣은 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 판정, log rename, `complete.log`, archive 이동은 code-review-skill 전용이다.

## 배경

`01_client_module_boundary`가 Flutter host 경로를 `apps/client`로 맞춘 뒤에도 Dart source는 flat `lib/models`, `lib/screens`, `lib/services` 구조다. Milestone은 `src/app`, `src/integrations`, `src/features` skeleton과 Web/Android 공통 bootstrap guard를 요구한다. 이 작업은 실제 network/protocol integration을 확장하기 전에 앱 entrypoint와 widget 테스트가 의존할 안정적인 구조를 만든다.

## 분석 결과

### 읽은 파일

- `agent-ops/roadmap/phase/project-workspace-management-ux/milestones/client-integration-standardization.md`
- `agent-ops/rules/project/domain/mobile/rules.md`
- `apps/mobile/lib/main.dart`
- `apps/mobile/lib/models/project_workspace.dart`
- `apps/mobile/lib/screens/workspace_home_page.dart`
- `apps/mobile/lib/services/mattermost_auth_service.dart`
- `apps/mobile/lib/services/workspace_launcher.dart`
- `apps/mobile/test/widget_test.dart`
- `apps/mobile/pubspec.yaml`
- `apps/mobile/analysis_options.yaml`
- `../alt/apps/client/lib/main.dart`
- `../alt/apps/client/lib/src/app/app.dart`
- `../alt/apps/client/lib/src/app/router.dart`
- `../alt/apps/client/README.md`

### 테스트 커버리지 공백

- App widget wiring: `apps/mobile/test/widget_test.dart:8-16` covers the rendered title and baseline project names. It must be updated to import the new app widget.
- Workspace launcher callback: `apps/mobile/test/widget_test.dart:18-42` covers callback plumbing and should remain.
- Bootstrap side effects: no test currently covers Firebase/plugin/fullscreen initialization. Add a unit-style test for bootstrap ordering with fakes only if the implementation introduces injectable bootstrap collaborators. Do not invoke Firebase or plugin channels in widget tests.

### 심볼 참조

- `MyApp`: references at `apps/mobile/lib/main.dart:45`, `apps/mobile/test/widget_test.dart:3`, `apps/mobile/test/widget_test.dart:11`. Rename to `NomadCodeClientApp` or keep a compatibility export only if tests need it.
- `MyHomePage`: references at `apps/mobile/lib/main.dart:60`, `apps/mobile/lib/main.dart:74`. Replace with app/root widget under `src/app`.
- `WorkspaceHomePage`: references at `apps/mobile/lib/main.dart:119`, `apps/mobile/test/widget_test.dart:4`, `apps/mobile/test/widget_test.dart:25`.
- `ProjectWorkspace`: references at `apps/mobile/lib/screens/workspace_home_page.dart:2`, `apps/mobile/lib/services/workspace_launcher.dart:2`, `apps/mobile/test/widget_test.dart:5`, `apps/mobile/test/widget_test.dart:21`.
- `WorkspaceLauncher`: references at `apps/mobile/lib/main.dart:7`, `apps/mobile/lib/main.dart:12`, `apps/mobile/lib/main.dart:121`.

### 분할 판단

split decision policy를 계획 파일 선택 전에 평가했다. 이 task는 `m-client-integration-standardization/02+01_client_bootstrap_skeleton`이며 predecessor는 `01_client_module_boundary`다. `01_client_module_boundary`의 `complete.log`가 없으면 시작하지 않는다. Dart skeleton은 path/rules 변경과 integration protocol 변경 사이의 별도 ownership boundary라 독립 plan으로 둔다.

### 범위 결정 근거

- `apps/client` 경로 이동은 완료된 predecessor 결과로 간주하고 이 task에서 다시 수행하지 않는다.
- proto-socket dependency 추가, endpoint config, lifecycle wrapper는 `03+02_integration_boundaries`가 소유한다.
- clone handoff 문서의 최종 checklist는 `04+03_clone_handoff_docs`가 소유한다.
- UI redesign은 하지 않는다. 기존 `WorkspaceHomePage` 동작과 test assertion을 유지한다.

### 빌드 등급

- build lane: `cloud-G06`, review lane: `cloud-G06`. 이유: Dart source 이동과 app/bootstrap symbol 변경이 widget tests와 imports에 영향을 주지만 storage/auth/protocol 구현은 아직 추가하지 않는다.

## 의존 관계 및 구현 순서

`01_client_module_boundary`가 PASS되어 `agent-task/.../01_client_module_boundary/complete.log`를 만든 뒤 시작한다. 이 dependency는 directory name `02+01_client_bootstrap_skeleton`의 source of truth와 일치한다.

## 구현 체크리스트

- [ ] [CIS-BOOT-1] `lib/src/app`, `lib/src/features`, `lib/src/integrations` skeleton으로 Dart files를 이동한다.
- [ ] [CIS-BOOT-2] side-effect가 분리된 client bootstrap entrypoint를 만든다.
- [ ] [CIS-BOOT-3] widget tests와 imports를 새 skeleton 기준으로 갱신한다.
- [ ] 중간 검증과 최종 검증 명령을 실행하고 실제 출력을 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [CIS-BOOT-1] Dart source skeleton 이동

#### 문제

현재 app source가 flat 구조다. `apps/mobile/lib/main.dart:6-8`이 `services`, `screens`를 직접 import하고 `apps/mobile/lib/screens/workspace_home_page.dart:2`는 `../models` relative import를 사용한다.

Before:

```dart
// apps/mobile/lib/main.dart:6-8
import 'services/mattermost_auth_service.dart';
import 'services/workspace_launcher.dart';
import 'screens/workspace_home_page.dart';
```

#### 해결 방법

predecessor 이후 경로 기준으로 파일을 아래처럼 이동한다.

```text
apps/client/lib/main.dart
apps/client/lib/src/app/bootstrap.dart
apps/client/lib/src/app/nomadcode_client_app.dart
apps/client/lib/src/features/workspaces/domain/project_workspace.dart
apps/client/lib/src/features/workspaces/presentation/workspace_home_page.dart
apps/client/lib/src/integrations/mattermost/mattermost_auth_service.dart
apps/client/lib/src/integrations/workspace/workspace_launcher.dart
```

Relative import는 package import 또는 `src` 내부 relative import로 정리한다. `src/app`은 app composition, `src/features/workspaces`는 workspace UI/domain, `src/integrations`는 host integration boundary만 소유한다.

#### 수정 파일 및 체크리스트

- [ ] `apps/client/lib/models/project_workspace.dart` -> `apps/client/lib/src/features/workspaces/domain/project_workspace.dart`
- [ ] `apps/client/lib/screens/workspace_home_page.dart` -> `apps/client/lib/src/features/workspaces/presentation/workspace_home_page.dart`
- [ ] `apps/client/lib/services/workspace_launcher.dart` -> `apps/client/lib/src/integrations/workspace/workspace_launcher.dart`
- [ ] `apps/client/lib/services/mattermost_auth_service.dart` -> `apps/client/lib/src/integrations/mattermost/mattermost_auth_service.dart`
- [ ] imports compile after move.

#### 테스트 작성

새 테스트는 작성하지 않는다. 기존 widget test를 새 import 경로로 갱신해 source move regression을 검증한다.

#### 중간 검증

```bash
cd apps/client && dart format --set-exit-if-changed lib test
```

예상: formatting diff 없음, exit code 0.

### [CIS-BOOT-2] side-effect 분리 bootstrap entrypoint

#### 문제

`apps/mobile/lib/main.dart:25-42`가 Flutter binding, fullscreen, Firebase, plugin initialize, Mattermost login, `runApp`을 한 함수에서 수행한다. 테스트 가능한 app widget과 platform side effect가 섞여 있다.

Before:

```dart
// apps/mobile/lib/main.dart:25-42
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await _applyFullscreenMode();
  await Firebase.initializeApp();
  await _pushService.initialize();
  final authService = MattermostAuthService(_pushService);
  try {
    await authService.autoLoginAndRegister();
  } catch (e) {
    print('[Main] Mattermost auto-login failed: $e');
  }
  runApp(const MyApp());
}
```

#### 해결 방법

`main.dart`는 얇게 유지한다. `bootstrap.dart`에 `Future<void> bootstrapNomadCodeClient()`와 `Future<void> runNomadCodeClient()`를 두고, app widget은 `NomadCodeClientApp`으로 분리한다. Bootstrap은 platform guard를 명시한다: `kIsWeb`에서도 Firebase initialization은 시도하되 plugin/native-only work는 plugin API가 지원하는 범위 안에서만 호출하고, 실패는 `debugPrint`로 기록한다.

After:

```dart
// apps/client/lib/main.dart
import 'src/app/bootstrap.dart';

Future<void> main() => runNomadCodeClient();
```

```dart
// apps/client/lib/src/app/bootstrap.dart
Future<void> runNomadCodeClient() async {
  WidgetsFlutterBinding.ensureInitialized();
  await bootstrapNomadCodeClient();
  runApp(const NomadCodeClientApp());
}
```

#### 수정 파일 및 체크리스트

- [ ] `apps/client/lib/main.dart`
- [ ] `apps/client/lib/src/app/bootstrap.dart`
- [ ] `apps/client/lib/src/app/nomadcode_client_app.dart`
- [ ] 기존 `_applyFullscreenMode`는 bootstrap 파일로 이동한다.
- [ ] `print` 신규 사용은 피하고 기존 bootstrap logging은 `debugPrint`로 전환한다.

#### 테스트 작성

Bootstrap full side effects는 channel/Firebase 의존으로 직접 테스트하지 않는다. 대신 app widget이 side-effect 없이 pump 가능한 구조를 widget test로 보장한다.

#### 중간 검증

```bash
cd apps/client && flutter test
```

예상: widget tests pass.

### [CIS-BOOT-3] widget tests 갱신

#### 문제

테스트는 `package:nomadcode_app/main.dart`, `package:nomadcode_app/screens/workspace_home_page.dart`, `package:nomadcode_app/models/project_workspace.dart`를 직접 import한다. 새 skeleton 후 이 imports는 깨진다.

Before:

```dart
// apps/mobile/test/widget_test.dart:3-5
import 'package:nomadcode_app/main.dart';
import 'package:nomadcode_app/screens/workspace_home_page.dart';
import 'package:nomadcode_app/models/project_workspace.dart';
```

#### 해결 방법

테스트 import를 `src/app/nomadcode_client_app.dart`, `src/features/workspaces/...` 경로로 바꾼다. 첫 테스트는 `NomadCodeClientApp`을 pump하고, 두 번째 테스트는 `WorkspaceHomePage` callback behavior를 유지한다.

After:

```dart
import 'package:nomadcode_app/src/app/nomadcode_client_app.dart';
import 'package:nomadcode_app/src/features/workspaces/domain/project_workspace.dart';
import 'package:nomadcode_app/src/features/workspaces/presentation/workspace_home_page.dart';
```

#### 수정 파일 및 체크리스트

- [ ] `apps/client/test/widget_test.dart`

#### 테스트 작성

기존 widget tests를 갱신한다. 새 test name은 기존 의도를 유지한다.

#### 중간 검증

```bash
cd apps/client && flutter test
```

예상: widget tests pass.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/client/lib/main.dart` | CIS-BOOT-2 |
| `apps/client/lib/src/app/bootstrap.dart` | CIS-BOOT-2 |
| `apps/client/lib/src/app/nomadcode_client_app.dart` | CIS-BOOT-2 |
| `apps/client/lib/src/features/workspaces/domain/project_workspace.dart` | CIS-BOOT-1 |
| `apps/client/lib/src/features/workspaces/presentation/workspace_home_page.dart` | CIS-BOOT-1 |
| `apps/client/lib/src/integrations/mattermost/mattermost_auth_service.dart` | CIS-BOOT-1 |
| `apps/client/lib/src/integrations/workspace/workspace_launcher.dart` | CIS-BOOT-1 |
| `apps/client/test/widget_test.dart` | CIS-BOOT-3 |

## 최종 검증

```bash
cd apps/client && dart format --set-exit-if-changed lib test
cd apps/client && flutter test
cd apps/client && flutter analyze --no-fatal-infos
bash -lc '! rg --sort path -n "package:nomadcode_app/(main|models|screens|services)" apps/client/lib apps/client/test'
```

마지막 search 명령은 옛 public import path가 남으면 실패한다. Go test cache 정책: 해당 없음. Flutter test cache 출력은 허용하지 않는다.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
