12 KiB
PLAN - CLIENT
이 파일을 읽는 구현 에이전트에게
CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 구현이 막히면 active review stub의 사용자 리뷰 요청에 정확한 결정/근거/실행 명령을 기록하고 멈춘다. 후속 에이전트가 재실행으로 메울 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니다.
배경
Flutter app bootstrap currently initializes Firebase and Mattermost push before rendering the operator shell. The current Milestone requires the operator shell to start without push/Firebase/Mattermost readiness, while push behavior remains a later Milestone. This plan separates core shell bootstrap from optional push integration without changing dashboard UI or generated contracts.
사용자 리뷰 요청 흐름
구현 중 사용자 결정이 필요한 경우 active CODE_REVIEW-*-G??.md의 사용자 리뷰 요청 섹션을 채우고 멈춘다. code-review가 검증한 경우에만 USER_REVIEW.md를 작성한다.
Roadmap Targets
- Milestone:
agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md - Task ids:
client-bootstrap: Flutter operator shell이 push/Firebase/Mattermost 초기화 없이도 시작 가능한 경계로 분리되고, push-specific integration은 후속 push Milestone 범위로 남는다.
- Completion mode: check-on-pass
분석 결과
읽은 파일
agent-ops/rules/project/rules.mdagent-ops/rules/private/rules.mdagent-ops/rules/common/rules-roadmap.mdagent-ops/rules/project/domain/client/rules.mdagent-ops/rules/project/domain/operations/rules.mdagent-test/local/rules.mdagent-test/local/client-smoke.mdagent-roadmap/current.mdagent-roadmap/phase/operator-surface/PHASE.mdagent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.mdapps/client/lib/main.dartapps/client/lib/src/app/bootstrap.dartapps/client/lib/src/app/app.dartapps/client/test/widget_test.dartapps/client/test/integrations/mattermost_push_host_integration_test.dartapps/client/integration_test/socket_runtime_smoke_test.dartapps/client/pubspec.yaml
테스트 환경 규칙
test_env=local.agent-test/local/rules.md는 존재하고 읽었다. local shell에서 테스트를 직접 실행하지 않고 remote host의 ALT checkout root에서 실행한다.- 매칭 profile은
agent-test/local/client-smoke.md다. client-only 변경 기본 명령은cd apps/client && flutter test. - Socket runtime/integration smoke는 API socket/platform runtime이 필요하면 실행 가능 여부를 확인하고, 실행할 수 없으면 pass가 아니라 skip/blocker와 미실행 명령을 기록한다.
테스트 커버리지 공백
apps/client/test/widget_test.dart:18already provesAltClientAppcan render without amattermostHost, but it bypassesrunAltClientandbootstrapAltClient.apps/client/lib/src/app/bootstrap.dart:25has no unit test proving Firebase/push are optional.- Mattermost host integration tests cover host initialization behavior in isolation (
apps/client/test/integrations/mattermost_push_host_integration_test.dart:91) but not app bootstrap separation.
심볼 참조
- renamed/removed symbol candidate: none required.
bootstrapAltClientreferences:apps/client/lib/src/app/bootstrap.dart:25,apps/client/lib/src/app/bootstrap.dart:33.runAltClientreferences:apps/client/lib/main.dart:3,apps/client/lib/src/app/bootstrap.dart:31.MattermostPushHostIntegrationapp references:apps/client/lib/src/app/bootstrap.dart:6,apps/client/lib/src/app/app.dart:6.
분할 판단
- split decision policy를 먼저 평가했다.
- shared task group:
m-operator-runtime-refactor. - this subtask:
03_client_bootstrap, no predecessors. - It is independent because it touches Flutter bootstrap/test boundaries only. It must not wait for worker/API runtime plans and must not implement push notification behavior itself.
범위 결정 근거
- Do not change dashboard layout, navigation depth, chart/form wireframe, or production operator screens.
- Do not edit generated Dart protobuf files.
- Do not remove Mattermost/Firebase integration packages; only make them optional at app bootstrap.
- Do not implement push notification behavior. Push-specific integration remains in the later push Milestone.
빌드 등급
- build lane:
cloud-G07; review lane:cloud-G07. - Flutter bootstrap touches platform/Firebase startup and optional external integration boundaries; tests are runnable but runtime smoke may be environment-gated.
구현 체크리스트
- [CLIENT-1] core operator shell bootstrap을 Firebase/Mattermost push initialization과 분리한다.
- [CLIENT-2] bootstrap tests를 추가해 기본
runAltClientpath가 push/Firebase 없이 app shell을 시작할 수 있음을 검증한다. - [CLIENT-3] push-specific initialization은 explicit opt-in API로 남기고 후속 push Milestone 범위임을 코드 경계로 드러낸다.
- 원격 ALT checkout root에서
cd apps/client && flutter test를 실행하고 실제 stdout/stderr를 review stub에 기록한다. - CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
[CLIENT-1] Core Shell Bootstrap
문제
apps/client/lib/src/app/bootstrap.dart:25 calls Firebase.initializeApp() and _mattermostHost.initialize() before runApp. This makes optional push readiness a prerequisite for the operator shell, contrary to the client domain rule and current Milestone.
해결 방법
Split bootstrap into core shell setup and optional push setup. Keep applyFullscreenMode in the core path. Return or pass a nullable MattermostPushHostIntegration only when push is explicitly enabled.
Before:
// apps/client/lib/src/app/bootstrap.dart:25
Future<void> bootstrapAltClient() async {
await applyFullscreenMode();
await Firebase.initializeApp();
await _mattermostHost.initialize();
}
After:
class AltClientBootstrapOptions {
const AltClientBootstrapOptions({this.enablePush = false});
final bool enablePush;
}
Future<MattermostPushHostIntegration?> bootstrapAltClient({
AltClientBootstrapOptions options = const AltClientBootstrapOptions(),
}) async {
await applyFullscreenMode();
if (!options.enablePush) return null;
await Firebase.initializeApp();
await _mattermostHost.initialize();
return _mattermostHost;
}
Allow injectable functions or host factory if needed for tests; do not call Firebase in the default test path.
수정 파일 및 체크리스트
apps/client/lib/src/app/bootstrap.dart: introduce bootstrap options/result and default push-off core bootstrap.apps/client/lib/main.dart: keepmainsimple; defaultrunAltClient()should render the core shell.apps/client/lib/src/app/app.dart: no change expected unless constructor docs or nullable host behavior needs tightening.
테스트 작성
Add apps/client/test/app/bootstrap_test.dart or equivalent:
- default
bootstrapAltClientdoes not call injected Firebase/push functions. - opt-in push path calls injected Firebase and host initialize once.
중간 검증
Run from the remote ALT checkout root:
cd apps/client && flutter test test/app/bootstrap_test.dart
Expected: exit code 0.
[CLIENT-2] App Shell Starts Without Push
문제
apps/client/test/widget_test.dart:19 instantiates AltClientApp directly, so it does not protect the runAltClient bootstrap path.
해결 방법
Make runAltClient accept injectable runApp/bootstrap hooks or a testable buildAltClientApp helper. Keep tests simple and avoid launching platform Firebase.
Before:
// apps/client/lib/src/app/bootstrap.dart:31
Future<void> runAltClient() async {
WidgetsFlutterBinding.ensureInitialized();
await bootstrapAltClient();
runApp(ProviderScope(child: AltClientApp(mattermostHost: _mattermostHost)));
}
After:
Future<void> runAltClient({
AltClientBootstrapOptions options = const AltClientBootstrapOptions(),
void Function(Widget app)? runAppOverride,
}) async {
WidgetsFlutterBinding.ensureInitialized();
final mattermostHost = await bootstrapAltClient(options: options);
(runAppOverride ?? runApp)(
ProviderScope(child: AltClientApp(mattermostHost: mattermostHost)),
);
}
If runAppOverride creates awkward production API, prefer a private testable helper that returns the root widget after bootstrap.
수정 파일 및 체크리스트
apps/client/lib/src/app/bootstrap.dart: make default run path passmattermostHost: null.apps/client/test/widget_test.dart: keep direct shell tests passing.apps/client/test/app/bootstrap_test.dart: assert default run path buildsAltClientAppwithout a Mattermost host.
테스트 작성
Required. Use injected callbacks/fakes so no Firebase platform initialization is needed.
중간 검증
Run from the remote ALT checkout root:
cd apps/client && flutter test test/widget_test.dart test/app/bootstrap_test.dart
Expected: exit code 0.
[CLIENT-3] Push Opt-In Boundary
문제
apps/client/lib/src/app/bootstrap.dart:10 creates a package-level _mattermostHost and immediately uses it during default bootstrap. This hides the optional boundary and invites later push work to leak into the operator shell.
해결 방법
Keep the host factory private but only instantiate or initialize it when push is explicitly enabled. Existing MattermostPushHostIntegration tests remain the behavior owner for push-specific logic.
Before:
// apps/client/lib/src/app/bootstrap.dart:10
final _mattermostHost = MattermostPushHostIntegration(
pushClient: MattermostPushPluginClient(),
);
After:
MattermostPushHostIntegration createMattermostPushHost() {
return MattermostPushHostIntegration(
pushClient: MattermostPushPluginClient(),
);
}
Use the factory only in opt-in push bootstrap. If a singleton is retained, ensure it is not initialized on the default path.
수정 파일 및 체크리스트
apps/client/lib/src/app/bootstrap.dart: isolate push host creation/initialization behind explicit opt-in.apps/client/test/integrations/mattermost_push_host_integration_test.dart: no behavior change expected; run to confirm.- Do not edit
apps/client/lib/src/integrations/mattermost/*unless a compile error requires a narrow import/API update.
테스트 작성
No new Mattermost behavior test is needed beyond existing host tests, but run them because imports/factory usage may change.
중간 검증
Run from the remote ALT checkout root:
cd apps/client && flutter test test/integrations/mattermost_push_host_integration_test.dart
Expected: exit code 0.
수정 파일 요약
| 파일 | 항목 |
|---|---|
apps/client/lib/src/app/bootstrap.dart |
CLIENT-1, CLIENT-2, CLIENT-3 |
apps/client/lib/main.dart |
CLIENT-2 |
apps/client/lib/src/app/app.dart |
CLIENT-1 if nullable host docs/types need tightening |
apps/client/test/app/bootstrap_test.dart |
CLIENT-1, CLIENT-2 |
apps/client/test/widget_test.dart |
CLIENT-2 |
apps/client/test/integrations/mattermost_push_host_integration_test.dart |
CLIENT-3 verification only |
최종 검증
Run from the remote ALT checkout root:
cd apps/client && flutter test
Expected: exit code 0. Flutter test cache is not accepted as evidence; record actual stdout/stderr.
If runtime socket smoke is attempted, use the client profile rules and record actual skip/blocker conditions instead of treating skipped integration as pass.
모든 코드 변경 완료 후 반드시 CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.