test(client-app): Flutter Web smoke 보완을 준비한다
브라우저 smoke의 선행 조건으로 test host를 web build 가능 상태로 만들고, 후속 runtime evidence 계획과 완료 로그를 함께 남긴다.
This commit is contained in:
parent
26e8d698d6
commit
e2823b38e0
17 changed files with 991 additions and 8 deletions
|
|
@ -40,6 +40,9 @@ Mattermost server/webapp/push-proxy는 upstream-followable runtime으로 두고,
|
||||||
- [완료] Flutter Web 알림
|
- [완료] Flutter Web 알림
|
||||||
- 경로: `agent-roadmap/archive/phase/messaging-runtime/milestones/flutter-web-notification.md`
|
- 경로: `agent-roadmap/archive/phase/messaging-runtime/milestones/flutter-web-notification.md`
|
||||||
- 요약: `packages/messaging_flutter`를 Flutter Web/Chrome foreground browser notification까지 확장하고, Full Web Push는 별도 후속 계약으로 분리한다.
|
- 요약: `packages/messaging_flutter`를 Flutter Web/Chrome foreground browser notification까지 확장하고, Full Web Push는 별도 후속 계약으로 분리한다.
|
||||||
|
- [계획] Flutter Web 알림 Smoke 보완
|
||||||
|
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/flutter-web-notification-smoke.md`
|
||||||
|
- 요약: Flutter Web foreground browser notification smoke가 원격 web runner와 forwarded localhost URL에서 실제로 재현되도록 host scaffold, Chrome 테스트, 수동 evidence 기준을 보완한다.
|
||||||
- [계획] iOS 알림 테스트
|
- [계획] iOS 알림 테스트
|
||||||
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/ios-notification-test.md`
|
- 경로: `agent-roadmap/phase/messaging-runtime/milestones/ios-notification-test.md`
|
||||||
- 요약: 선행 정리 후 iOS/APNs 권한, token, open routing, ACK/dismiss 계약을 실제 설정과 smoke 기준으로 검증한다.
|
- 요약: 선행 정리 후 iOS/APNs 권한, token, open routing, ACK/dismiss 계약을 실제 설정과 smoke 기준으로 검증한다.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
# Milestone: Flutter Web 알림 Smoke 보완
|
||||||
|
|
||||||
|
## 위치
|
||||||
|
|
||||||
|
- Roadmap: `agent-roadmap/ROADMAP.md`
|
||||||
|
- Phase: `agent-roadmap/phase/messaging-runtime/PHASE.md`
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
|
||||||
|
Flutter Web foreground browser notification이 실제 브라우저 환경에서 반복 검증될 수 있게 한다.
|
||||||
|
기존 API와 fake interop 단위 테스트 근거만으로 완료 처리된 gap을 보완하고, 원격 runner에서 열린 Flutter Web server를 사용자가 forwarded localhost URL로 접근해 permission, display, click/open routing을 확인할 수 있는 smoke 기준을 만든다.
|
||||||
|
|
||||||
|
## 상태
|
||||||
|
|
||||||
|
[계획]
|
||||||
|
|
||||||
|
## 승격 조건
|
||||||
|
|
||||||
|
- 없음
|
||||||
|
|
||||||
|
## 구현 잠금
|
||||||
|
|
||||||
|
- 상태: 해제
|
||||||
|
- 결정 필요: 없음
|
||||||
|
|
||||||
|
## 범위
|
||||||
|
|
||||||
|
- `apps/flutter-test`를 Flutter Web build/run 대상 host scaffold로 구성한다.
|
||||||
|
- `packages/messaging_flutter`의 VM 단위 테스트와 Chrome/web 테스트를 분리해 각 platform expectation이 충돌하지 않게 정리한다.
|
||||||
|
- 원격 runner의 Flutter Web server와 사용자가 여는 forwarded localhost URL을 전제로 browser notification permission, display, click/open routing smoke 절차를 만든다.
|
||||||
|
- `bin/test`, `bin/lint`, `bin/build` 또는 별도 opt-in 명령에서 web smoke/build 검증이 누락되지 않도록 workspace helper와 테스트 기준을 보강한다.
|
||||||
|
|
||||||
|
## 기능
|
||||||
|
|
||||||
|
### Epic: [web-host] Web 테스트 Host
|
||||||
|
|
||||||
|
Flutter Web 알림 smoke가 실제 브라우저에서 열릴 수 있도록 test host와 실행 경계를 만든다.
|
||||||
|
|
||||||
|
- [ ] [web-scaffold] `apps/flutter-test`에 Flutter Web scaffold를 추가하고 web build가 통과하도록 구성한다. 검증: 원격 runner에서 `apps/flutter-test` web build가 성공해야 한다.
|
||||||
|
- [ ] [web-runtime] 원격 Flutter Web server와 forwarded localhost URL 기반 수동 smoke 절차를 정리한다. 검증: 사용자가 forwarded URL에서 permission 요청, notification display, click/open routing 상태를 확인할 수 있어야 한다.
|
||||||
|
|
||||||
|
### Epic: [web-tests] Web 검증 분리
|
||||||
|
|
||||||
|
VM 단위 테스트와 Chrome/web 테스트가 서로 다른 platform expectation을 올바르게 검증하도록 나눈다.
|
||||||
|
|
||||||
|
- [ ] [chrome-tests] `packages/messaging_flutter`의 Chrome/web 테스트가 web runtime에서는 browser notification support를 기대하고, VM 테스트는 non-web unsupported를 기대하도록 분리한다. 검증: 원격 runner에서 package Chrome 테스트가 통과해야 한다.
|
||||||
|
- [ ] [helper-route] workspace helper 또는 테스트 규칙에 web build/smoke opt-in 경로를 추가해 `bin/test`만으로 web smoke가 완료된 것처럼 보이지 않게 한다. 검증: web 검증을 생략한 경우 skip 또는 미실행 사유가 명확히 보고되어야 한다.
|
||||||
|
|
||||||
|
### Epic: [evidence] 완료 Evidence
|
||||||
|
|
||||||
|
완료 판정이 fake interop 단위 테스트에만 기대지 않도록 browser evidence 기준을 남긴다.
|
||||||
|
|
||||||
|
- [ ] [manual-evidence] browser notification permission/display/click 수동 smoke evidence 기준을 민감값 없이 정리한다. 검증: 완료 리뷰에 원격 실행 명령, 사용자 확인 결과, 생략된 Full Web Push 범위가 함께 남아야 한다.
|
||||||
|
|
||||||
|
## 완료 리뷰
|
||||||
|
|
||||||
|
- 상태: 없음
|
||||||
|
- 요청일: 없음
|
||||||
|
- 완료 근거: 없음
|
||||||
|
- 리뷰 필요:
|
||||||
|
- [ ] 사용자가 완료 결과를 확인했다
|
||||||
|
- [ ] archive 이동을 승인했다
|
||||||
|
- 리뷰 코멘트: 없음
|
||||||
|
|
||||||
|
## 범위 제외
|
||||||
|
|
||||||
|
- 브라우저가 닫힌 상태에서도 수신되는 Full Web Push 구현
|
||||||
|
- service worker, VAPID, `web_rn-v2` server device-token 등록, core/push-proxy web target 구현
|
||||||
|
- Mattermost webapp UI/Redux 알림 구현 복제
|
||||||
|
- 제품 앱 UI 또는 앱별 메시징 화면 구현
|
||||||
|
|
||||||
|
## 작업 컨텍스트
|
||||||
|
|
||||||
|
- 관련 경로: `packages/messaging_flutter/`, `apps/flutter-test/`, `bin/`, `agent-test/local/`
|
||||||
|
- 표준선(선택): 실제 host, port, credential, 포워딩 명령 원문은 tracked roadmap/docs에 쓰지 않고 ignored local test environment 문서 또는 사용자 제공 환경 기준에서 확인한다.
|
||||||
|
- 선행 작업: Flutter Web 알림
|
||||||
|
- 후속 작업: Full Web Push platform 계약
|
||||||
|
- 확인 필요: 원격 runner에서 Chrome/web device와 사용자가 접근 가능한 forwarded localhost URL을 함께 열어 실제 browser permission/display/click evidence를 남긴다.
|
||||||
|
|
@ -0,0 +1,188 @@
|
||||||
|
<!-- task=m-flutter-web-notification-smoke/01_web_scaffold plan=0 tag=WEB-SCAFFOLD -->
|
||||||
|
|
||||||
|
# Code Review Reference - WEB-SCAFFOLD
|
||||||
|
|
||||||
|
> **[IMPLEMENTING AGENT - READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||||
|
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||||
|
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||||
|
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||||
|
> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`.
|
||||||
|
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation.
|
||||||
|
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only.
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
date=2026-06-08
|
||||||
|
task=m-flutter-web-notification-smoke/01_web_scaffold, plan=0, tag=WEB-SCAFFOLD
|
||||||
|
|
||||||
|
## Roadmap Targets
|
||||||
|
|
||||||
|
- Milestone: `agent-roadmap/phase/messaging-runtime/milestones/flutter-web-notification-smoke.md`
|
||||||
|
- Task ids:
|
||||||
|
- `web-scaffold`: `apps/flutter-test`에 Flutter Web scaffold를 추가하고 web build가 통과하도록 구성한다.
|
||||||
|
- Completion mode: check-on-pass
|
||||||
|
|
||||||
|
## 이 파일을 읽는 리뷰 에이전트에게
|
||||||
|
|
||||||
|
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||||
|
|
||||||
|
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 구현 항목별 완료 여부
|
||||||
|
|
||||||
|
| 항목 | 완료 여부 |
|
||||||
|
|------|---------|
|
||||||
|
| [WEB-SCAFFOLD-1] Web scaffold 추가 | [x] |
|
||||||
|
| [WEB-SCAFFOLD-2] Firebase web init fallback | [x] |
|
||||||
|
| [WEB-SCAFFOLD-3] README web host 기준 반영 | [x] |
|
||||||
|
|
||||||
|
## 구현 체크리스트
|
||||||
|
|
||||||
|
- [x] `apps/flutter-test`에 Flutter Web scaffold를 추가한다.
|
||||||
|
- [x] web 실행 시 Firebase config 부재가 test host UI 자체를 막지 않도록 `main()` 초기화를 안전하게 조정한다.
|
||||||
|
- [x] `apps/flutter-test/README.md`의 Structure/Verification에 web host와 remote web build 기준을 반영하되 host/port 원문은 쓰지 않는다.
|
||||||
|
- [x] 원격 runner 기준으로 `apps/flutter-test` web build가 통과하는지 검증한다.
|
||||||
|
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||||
|
|
||||||
|
## 코드리뷰 전용 체크리스트
|
||||||
|
|
||||||
|
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||||
|
|
||||||
|
- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||||
|
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||||
|
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다.
|
||||||
|
- [x] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다.
|
||||||
|
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||||
|
- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||||
|
- [x] PASS이면 active task 디렉터리 `agent-task/{task_name}/`를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||||
|
- [x] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||||
|
- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||||
|
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||||
|
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||||
|
- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||||
|
|
||||||
|
## 구현 에이전트 기록
|
||||||
|
|
||||||
|
### 변경 요약
|
||||||
|
|
||||||
|
- `apps/flutter-test`에 Flutter-generated web scaffold(`web/index.html`, `web/manifest.json`, favicon, icons)와 `.metadata` web platform metadata를 추가했다.
|
||||||
|
- `main()`의 Firebase 초기화를 `_initializeFirebaseForHarness()` best-effort helper로 분리해 Firebase web config가 없어도 harness UI가 렌더링되게 했다.
|
||||||
|
- `apps/flutter-test/README.md`에 `web/` host scaffold와 `flutter build web` 검증 기준을 추가하고, 실제 host/port/credential/forwarding 값은 tracked README에 쓰지 않는 기준으로 정리했다.
|
||||||
|
|
||||||
|
### 계획 대비 변경 사항
|
||||||
|
|
||||||
|
- `agent-test/local/web-smoke.env`가 없어 최종 원격 명령은 local shell 변수로만 runner 값을 주입해 실행했다. tracked 파일에는 실제 원격 값을 기록하지 않았다.
|
||||||
|
- 원격 checkout이 dirty 상태라 직접 덮지 않고 현재 checkout을 `/tmp/nexo-web-scaffold.solJRQ` 임시 디렉터리에 복사해 검증했다.
|
||||||
|
|
||||||
|
### 검증 결과
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ cd apps/flutter-test && flutter pub get
|
||||||
|
Resolving dependencies...
|
||||||
|
Downloading packages...
|
||||||
|
Got dependencies!
|
||||||
|
11 packages have newer versions incompatible with dependency constraints.
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ cd apps/flutter-test && flutter analyze --no-fatal-infos
|
||||||
|
Analyzing flutter-test...
|
||||||
|
No issues found! (ran in 4.1s)
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ cd apps/flutter-test && flutter test
|
||||||
|
00:00 +0: loading /config/workspace/nexo/apps/flutter-test/test/widget_test.dart
|
||||||
|
00:00 +9: All tests passed!
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ cd apps/flutter-test && flutter build web
|
||||||
|
Compiling lib/main.dart for the Web...
|
||||||
|
Wasm dry run succeeded. Consider building and testing your application with the `--wasm` flag.
|
||||||
|
Compiling lib/main.dart for the Web... 28.7s
|
||||||
|
✓ Built build/web
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ cd apps/flutter-test && rg --sort path -n "web|browser|remote" README.md
|
||||||
|
57: web/ # Web test host scaffold for browser smoke checks
|
||||||
|
75:Android/iOS/device smoke tests and browser previews should receive runtime URLs
|
||||||
|
120:flutter build web
|
||||||
|
125:event-channel, and opened-routing coverage. Use `flutter build web` to verify
|
||||||
|
126:that the web host scaffold remains buildable before browser notification smoke
|
||||||
|
127:checks run on the remote runner.
|
||||||
|
```
|
||||||
|
|
||||||
|
원격 checkout 상태 확인 결과 기존 변경이 있어 임시 복사본에서 검증했다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ ssh "$NEXO_REMOTE_SSH" "zsh -lc 'cd ${NEXO_REMOTE_REPO} && git status --short'"
|
||||||
|
M README.md
|
||||||
|
M apps/flutter-test/pubspec.lock
|
||||||
|
M apps/mattermost/android/app/build.gradle
|
||||||
|
M bin/test
|
||||||
|
M docs/runtime-image-validation.md
|
||||||
|
M services/core/compose/README.md
|
||||||
|
?? agent-task/m-cicd-operations/
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ tar -czf - --exclude='.git' --exclude='*/build' --exclude='*/.dart_tool' --exclude='*/node_modules' --exclude='./agent-task/archive' --exclude='./agent-roadmap/archive' . | ssh "$NEXO_REMOTE_SSH" "tar -xzf - -C '$NEXO_REMOTE_TMP'"
|
||||||
|
# NEXO_REMOTE_TMP=/tmp/nexo-web-scaffold.solJRQ
|
||||||
|
# exit code 0
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ ssh "$NEXO_REMOTE_SSH" "zsh -lc 'cd ${NEXO_REMOTE_TMP}/apps/flutter-test && flutter pub get && flutter analyze --no-fatal-infos && flutter test && flutter build web'"
|
||||||
|
Resolving dependencies...
|
||||||
|
Changed 2 dependencies!
|
||||||
|
11 packages have newer versions incompatible with dependency constraints.
|
||||||
|
The following plugins do not support Swift Package Manager for ios:
|
||||||
|
- nexo_messaging
|
||||||
|
The following plugins do not support Swift Package Manager for macos:
|
||||||
|
- nexo_messaging
|
||||||
|
Analyzing flutter-test...
|
||||||
|
No issues found! (ran in 1.6s)
|
||||||
|
00:00 +0: loading /private/tmp/nexo-web-scaffold.solJRQ/apps/flutter-test/test/widget_test.dart
|
||||||
|
00:00 +9: All tests passed!
|
||||||
|
Compiling lib/main.dart for the Web...
|
||||||
|
Wasm dry run succeeded. Consider building and testing your application with the `--wasm` flag.
|
||||||
|
Compiling lib/main.dart for the Web... 22.1s
|
||||||
|
✓ Built build/web
|
||||||
|
```
|
||||||
|
|
||||||
|
### 남은 위험
|
||||||
|
|
||||||
|
- Browser notification permission/display/click 수동 smoke evidence는 계획대로 후속 `02+01_web_runtime` 범위다.
|
||||||
|
- 원격 Flutter는 iOS/macOS Swift Package Manager 미지원 경고를 출력했지만 현재 검증 명령은 exit code 0으로 통과했다.
|
||||||
|
|
||||||
|
## 사용자 리뷰 요청
|
||||||
|
|
||||||
|
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||||
|
|
||||||
|
- 상태: 없음
|
||||||
|
- 사유 유형: 없음
|
||||||
|
- 결정 필요: 없음
|
||||||
|
- 차단 근거: 없음
|
||||||
|
- 실행한 검증/명령: 없음
|
||||||
|
- 자동 후속 불가 이유: 없음
|
||||||
|
- 재개 조건: 없음
|
||||||
|
|
||||||
|
## 코드리뷰 결과
|
||||||
|
|
||||||
|
### 2026-06-08 리뷰
|
||||||
|
|
||||||
|
- 종합 판정: PASS
|
||||||
|
- 차원별 평가:
|
||||||
|
- Correctness: Pass
|
||||||
|
- Completeness: Pass
|
||||||
|
- Test coverage: Pass
|
||||||
|
- API contract: Pass
|
||||||
|
- Code quality: Pass
|
||||||
|
- Plan deviation: Pass
|
||||||
|
- Verification trust: Pass
|
||||||
|
- 발견된 문제: 없음
|
||||||
|
- 다음 단계: PASS이므로 `complete.log`를 작성하고 active task directory를 archive로 이동한다.
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Complete - m-flutter-web-notification-smoke/01_web_scaffold
|
||||||
|
|
||||||
|
## 완료 일시
|
||||||
|
|
||||||
|
2026-06-08
|
||||||
|
|
||||||
|
## 요약
|
||||||
|
|
||||||
|
Flutter Web scaffold 보완 작업을 1회 리뷰했고 최종 판정은 PASS다.
|
||||||
|
|
||||||
|
## 루프 이력
|
||||||
|
|
||||||
|
| Plan | Review | Verdict | 메모 |
|
||||||
|
|------|--------|---------|------|
|
||||||
|
| `plan_local_G06_0.log` | `code_review_local_G06_0.log` | PASS | Web scaffold, Firebase init fallback, README web build 기준이 계획대로 반영됐고 검증이 통과했다. |
|
||||||
|
|
||||||
|
## 구현/정리 내용
|
||||||
|
|
||||||
|
- `apps/flutter-test/web/` Flutter Web scaffold와 `.metadata` web platform metadata를 추가했다.
|
||||||
|
- `apps/flutter-test/lib/main.dart`에서 Firebase 초기화를 harness best-effort helper로 분리해 config 부재 시에도 UI가 렌더링되게 했다.
|
||||||
|
- `apps/flutter-test/README.md`에 web host scaffold와 `flutter build web` 검증 기준을 반영했다.
|
||||||
|
|
||||||
|
## 최종 검증
|
||||||
|
|
||||||
|
- `flutter pub get` - PASS; `Got dependencies!`, 11개 newer-version 안내만 출력.
|
||||||
|
- `flutter analyze --no-fatal-infos` - PASS; `No issues found!`.
|
||||||
|
- `flutter test` - PASS; `00:00 +9: All tests passed!`.
|
||||||
|
- `flutter build web` - PASS; `Built build/web`.
|
||||||
|
- 원격 임시 복사본 검증 - PASS; `flutter pub get && flutter analyze --no-fatal-infos && flutter test && flutter build web`가 구현 로그 기준 exit code 0으로 완료됐다.
|
||||||
|
|
||||||
|
## Roadmap Completion
|
||||||
|
|
||||||
|
- Milestone: `agent-roadmap/phase/messaging-runtime/milestones/flutter-web-notification-smoke.md`
|
||||||
|
- Completed task ids:
|
||||||
|
- `web-scaffold`: PASS; evidence=`plan_local_G06_0.log`, `code_review_local_G06_0.log`; verification=`flutter pub get`, `flutter analyze --no-fatal-infos`, `flutter test`, `flutter build web`, remote temporary-copy verification
|
||||||
|
- Not completed task ids: 없음
|
||||||
|
|
||||||
|
## 잔여 Nit
|
||||||
|
|
||||||
|
- 없음
|
||||||
|
|
||||||
|
## 후속 작업
|
||||||
|
|
||||||
|
- 없음
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
<!-- task=m-flutter-web-notification-smoke/01_web_scaffold plan=0 tag=WEB-SCAFFOLD -->
|
||||||
|
|
||||||
|
# Implementation Plan - WEB-SCAFFOLD
|
||||||
|
|
||||||
|
## 이 파일을 읽는 구현 에이전트에게
|
||||||
|
|
||||||
|
`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것이 필수다. 구현 후 검증을 실행하고 실제 출력과 변경 내용을 채운 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경, scope 충돌 때문에 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 쓰고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 최종 archive, `complete.log`, roadmap 반영은 code-review/runtime 책임이다.
|
||||||
|
|
||||||
|
## 배경
|
||||||
|
|
||||||
|
`Flutter Web 알림`은 완료 처리됐지만 `apps/flutter-test`가 web project로 구성되어 있지 않아 원격 runner에서 `flutter build web`이 실패한다. 첫 Epic의 선행 작업은 test host가 실제 Flutter Web build/run 대상이 되게 만드는 것이다. 이 작업은 `web-runtime` 수동 smoke의 선행 조건이다.
|
||||||
|
|
||||||
|
## 사용자 리뷰 요청 흐름
|
||||||
|
|
||||||
|
구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 직접 사용자 질문은 금지하며, code-review가 blocker 타당성을 검증하고 필요할 때만 `USER_REVIEW.md`를 만든다.
|
||||||
|
|
||||||
|
## Roadmap Targets
|
||||||
|
|
||||||
|
- Milestone: `agent-roadmap/phase/messaging-runtime/milestones/flutter-web-notification-smoke.md`
|
||||||
|
- Task ids:
|
||||||
|
- `web-scaffold`: `apps/flutter-test`에 Flutter Web scaffold를 추가하고 web build가 통과하도록 구성한다.
|
||||||
|
- Completion mode: check-on-pass
|
||||||
|
|
||||||
|
## 분석 결과
|
||||||
|
|
||||||
|
### 읽은 파일
|
||||||
|
|
||||||
|
- `agent-roadmap/current.md`
|
||||||
|
- `agent-roadmap/phase/messaging-runtime/PHASE.md`
|
||||||
|
- `agent-roadmap/phase/messaging-runtime/milestones/flutter-web-notification-smoke.md`
|
||||||
|
- `agent-ops/rules/project/domain/client-app/rules.md`
|
||||||
|
- `agent-ops/rules/project/domain/workspace-ops/rules.md`
|
||||||
|
- `agent-test/local/rules.md`
|
||||||
|
- `agent-test/local/client-app-smoke.md`
|
||||||
|
- `agent-test/local/runtime-env.md`
|
||||||
|
- `apps/flutter-test/pubspec.yaml`
|
||||||
|
- `apps/flutter-test/lib/main.dart`
|
||||||
|
- `apps/flutter-test/test/widget_test.dart`
|
||||||
|
- `apps/flutter-test/README.md`
|
||||||
|
|
||||||
|
### 테스트 환경 규칙
|
||||||
|
|
||||||
|
사용자 지시상 local 수용 검증은 사용하지 않는다. 규칙 파일명은 `agent-test/local/rules.md`지만, 이번 검증은 이 규칙의 `runtime-env` profile에 있는 standard remote runner를 사용한다. `client-app-smoke`의 기본 명령은 `flutter pub get`, `flutter analyze --no-fatal-infos`, `flutter test`이며, web build는 repository manifest fallback으로 `flutter build web`을 추가한다. 실제 host, port, credential 값은 tracked plan/review에 쓰지 않고 ignored environment profile에서 변수로 주입한다.
|
||||||
|
|
||||||
|
### 테스트 커버리지 공백
|
||||||
|
|
||||||
|
- Web scaffold 부재: 기존 widget tests는 `MyApp` UI만 검증하고 web platform scaffold나 `flutter build web`을 검증하지 않는다.
|
||||||
|
- Firebase web config 부재: `apps/flutter-test/lib/main.dart:7-10`은 `Firebase.initializeApp()` 실패 시 앱 UI를 띄우지 못할 수 있다. 기존 tests는 `main()`을 실행하지 않으므로 이 gap을 잡지 못한다.
|
||||||
|
- Browser notification permission/display/click 수동 smoke는 후속 `02+01_web_runtime` 계획에서 다룬다.
|
||||||
|
|
||||||
|
### 심볼 참조
|
||||||
|
|
||||||
|
Renamed/removed symbols: none. 새 helper를 추가할 경우 call site는 `main()` 하나다.
|
||||||
|
|
||||||
|
### 분할 판단
|
||||||
|
|
||||||
|
Split policy 적용 결과, 첫 Epic은 두 subtask로 분리한다. 공유 task group은 `agent-task/m-flutter-web-notification-smoke/`다.
|
||||||
|
|
||||||
|
- `01_web_scaffold`: web scaffold와 web build 가능 상태를 만든다. 독립 선행 작업.
|
||||||
|
- `02+01_web_runtime`: 사용자가 접근하는 forwarded browser smoke 절차와 evidence를 다룬다. `01_web_scaffold` 완료 후 실행한다.
|
||||||
|
|
||||||
|
분리 이유: generated platform scaffold와 interactive browser evidence는 위험과 검증 방식이 다르다. user-owned browser/port-forward evidence는 별도 plan에서 다루는 것이 review 가능하다.
|
||||||
|
|
||||||
|
### 범위 결정 근거
|
||||||
|
|
||||||
|
이 계획은 `apps/flutter-test` web host scaffold와 web build 가능성만 다룬다. `packages/messaging_flutter` Chrome test 분리, workspace helper opt-in, Full Web Push, service worker, VAPID, core/push-proxy web target은 제외한다. 원격 preview run과 사용자 수동 smoke evidence는 dependent subtask가 담당한다.
|
||||||
|
|
||||||
|
### 빌드 등급
|
||||||
|
|
||||||
|
Build lane/grade: `local-G06`. Generated web scaffold와 Firebase web init fallback이 포함되지만 범위는 `apps/flutter-test`로 제한되고 원격 web build로 검증 가능하다.
|
||||||
|
|
||||||
|
## 구현 체크리스트
|
||||||
|
|
||||||
|
- [ ] `apps/flutter-test`에 Flutter Web scaffold를 추가한다.
|
||||||
|
- [ ] web 실행 시 Firebase config 부재가 test host UI 자체를 막지 않도록 `main()` 초기화를 안전하게 조정한다.
|
||||||
|
- [ ] `apps/flutter-test/README.md`의 Structure/Verification에 web host와 remote web build 기준을 반영하되 host/port 원문은 쓰지 않는다.
|
||||||
|
- [ ] 원격 runner 기준으로 `apps/flutter-test` web build가 통과하는지 검증한다.
|
||||||
|
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||||
|
|
||||||
|
### [WEB-SCAFFOLD-1] Web scaffold 추가
|
||||||
|
|
||||||
|
#### 문제
|
||||||
|
|
||||||
|
`apps/flutter-test` 하위에 `web/` 디렉터리가 없다. 현재 `flutter build web`은 `This project is not configured for the web.`로 실패한다.
|
||||||
|
|
||||||
|
#### 해결 방법
|
||||||
|
|
||||||
|
`apps/flutter-test`에서 Flutter web scaffold만 생성한다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd apps/flutter-test
|
||||||
|
flutter create . --platforms web --project-name nexo_client
|
||||||
|
```
|
||||||
|
|
||||||
|
생성 결과에서 Android/iOS/macOS/Linux/Windows 파일이 변경되면 web scaffold와 직접 관련 없는 generated churn이므로 되돌리지 말고 먼저 diff를 확인하고, 불필요한 대량 변경은 구현 범위에서 제외한다. 유지 대상은 `apps/flutter-test/web/**`와 web build에 필요한 최소 metadata다.
|
||||||
|
|
||||||
|
#### 수정 파일 및 체크리스트
|
||||||
|
|
||||||
|
- [ ] `apps/flutter-test/web/index.html`
|
||||||
|
- [ ] `apps/flutter-test/web/manifest.json`
|
||||||
|
- [ ] `apps/flutter-test/web/favicon.png`
|
||||||
|
- [ ] `apps/flutter-test/web/icons/**`
|
||||||
|
- [ ] `apps/flutter-test/.metadata`가 생성/갱신되면 web scaffold에 필요한지 diff로 확인한다.
|
||||||
|
|
||||||
|
#### 테스트 작성
|
||||||
|
|
||||||
|
새 Dart test는 작성하지 않는다. 이 항목은 generated platform scaffold 추가이며 pass/fail은 web build로 판정한다.
|
||||||
|
|
||||||
|
#### 중간 검증
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd apps/flutter-test && flutter build web
|
||||||
|
```
|
||||||
|
|
||||||
|
기대 결과: web project 구성 오류 없이 build가 성공한다.
|
||||||
|
|
||||||
|
### [WEB-SCAFFOLD-2] Firebase web init fallback
|
||||||
|
|
||||||
|
#### 문제
|
||||||
|
|
||||||
|
`apps/flutter-test/lib/main.dart:7-10`은 앱 UI를 띄우기 전에 `Firebase.initializeApp()`을 await한다.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Future<void> main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
await Firebase.initializeApp();
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Web smoke는 민감값 없는 foreground notification UI를 확인하는 것이 목적이다. Firebase web config가 없는 상태에서 초기화 실패가 앱 렌더링을 막으면 permission/display smoke controls에 접근할 수 없다.
|
||||||
|
|
||||||
|
#### 해결 방법
|
||||||
|
|
||||||
|
Firebase 초기화를 harness best-effort helper로 분리하고 실패해도 UI는 렌더링한다. 기존 `_initializePlugin()`의 try/catch처럼 test host가 상태 UI를 계속 보여주게 한다.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Future<void> main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
await _initializeFirebaseForHarness();
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _initializeFirebaseForHarness() async {
|
||||||
|
try {
|
||||||
|
await Firebase.initializeApp();
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
debugPrint('[NexoClient] Firebase initialization skipped: $error');
|
||||||
|
debugPrintStack(stackTrace: stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 수정 파일 및 체크리스트
|
||||||
|
|
||||||
|
- [ ] `apps/flutter-test/lib/main.dart`
|
||||||
|
|
||||||
|
#### 테스트 작성
|
||||||
|
|
||||||
|
별도 mock Firebase test는 작성하지 않는다. `test/widget_test.dart`는 `MyApp(initializePlugin: false)`를 직접 pump해 UI contract를 검증한다. 실제 web startup은 build와 후속 runtime smoke로 검증한다.
|
||||||
|
|
||||||
|
#### 중간 검증
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd apps/flutter-test && flutter test
|
||||||
|
cd apps/flutter-test && flutter build web
|
||||||
|
```
|
||||||
|
|
||||||
|
기대 결과: widget tests PASS, web build PASS.
|
||||||
|
|
||||||
|
### [WEB-SCAFFOLD-3] README web host 기준 반영
|
||||||
|
|
||||||
|
#### 문제
|
||||||
|
|
||||||
|
`apps/flutter-test/README.md:47-57`의 구조 목록에 `web/`이 없고, `apps/flutter-test/README.md:73-76`은 browser preview가 "added later"라고 되어 있다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
apps/flutter-test/
|
||||||
|
lib/ # Small test harness UI
|
||||||
|
test/ # Widget tests for the harness
|
||||||
|
integration_test/ # Device/emulator plugin integration tests
|
||||||
|
android/ # Android test host used to load the plugin
|
||||||
|
ios/ # iOS host scaffold
|
||||||
|
macos/ # macOS host scaffold
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 해결 방법
|
||||||
|
|
||||||
|
README에 web scaffold와 remote-runner smoke 기준을 추가한다. 실제 host, port, credential, forwarding command 값은 tracked README에 쓰지 않는다. ignored environment profile 또는 사용자 제공 환경에서 확인한다고 적는다.
|
||||||
|
|
||||||
|
#### 수정 파일 및 체크리스트
|
||||||
|
|
||||||
|
- [ ] `apps/flutter-test/README.md`
|
||||||
|
|
||||||
|
#### 테스트 작성
|
||||||
|
|
||||||
|
문서 변경은 테스트를 추가하지 않는다.
|
||||||
|
|
||||||
|
#### 중간 검증
|
||||||
|
|
||||||
|
```sh
|
||||||
|
rg --sort path -n "web|browser|remote" apps/flutter-test/README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
기대 결과: web host 기준이 README에 있고 민감한 환경값 원문이 없다.
|
||||||
|
|
||||||
|
## 수정 파일 요약
|
||||||
|
|
||||||
|
| 파일 | 항목 |
|
||||||
|
| --- | --- |
|
||||||
|
| `apps/flutter-test/web/**` | WEB-SCAFFOLD-1 |
|
||||||
|
| `apps/flutter-test/.metadata` | WEB-SCAFFOLD-1 |
|
||||||
|
| `apps/flutter-test/lib/main.dart` | WEB-SCAFFOLD-2 |
|
||||||
|
| `apps/flutter-test/README.md` | WEB-SCAFFOLD-3 |
|
||||||
|
|
||||||
|
## 최종 검증
|
||||||
|
|
||||||
|
아래 명령은 ignored environment profile에서 `NEXO_REMOTE_SSH`와 `NEXO_REMOTE_REPO`를 주입한 뒤 실행한다. 실제 값은 tracked 파일에 기록하지 않는다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
set -a; source agent-test/local/web-smoke.env; set +a
|
||||||
|
ssh "$NEXO_REMOTE_SSH" "zsh -lc 'cd ${NEXO_REMOTE_REPO}/apps/flutter-test && flutter pub get && flutter analyze --no-fatal-infos && flutter test && flutter build web'"
|
||||||
|
```
|
||||||
|
|
||||||
|
기대 결과: 모든 명령 exit code 0. 원격 checkout이 dirty여서 현재 작업 diff를 반영할 수 없으면 구현 에이전트는 현재 checkout을 원격 임시 디렉터리에 복사해 같은 명령을 실행하고, 복사 경로와 명령 출력을 review stub에 기록한다.
|
||||||
|
|
||||||
|
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
<!-- task=m-flutter-web-notification-smoke/02+01_web_runtime plan=0 tag=WEB-RUNTIME -->
|
||||||
|
|
||||||
|
# Code Review Reference - WEB-RUNTIME
|
||||||
|
|
||||||
|
> **[IMPLEMENTING AGENT - READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||||
|
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||||
|
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||||
|
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||||
|
> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`.
|
||||||
|
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation.
|
||||||
|
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only.
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
date=2026-06-08
|
||||||
|
task=m-flutter-web-notification-smoke/02+01_web_runtime, plan=0, tag=WEB-RUNTIME
|
||||||
|
|
||||||
|
## Roadmap Targets
|
||||||
|
|
||||||
|
- Milestone: `agent-roadmap/phase/messaging-runtime/milestones/flutter-web-notification-smoke.md`
|
||||||
|
- Task ids:
|
||||||
|
- `web-runtime`: 원격 Flutter Web server와 forwarded localhost URL 기반 수동 smoke 절차를 정리한다.
|
||||||
|
- Completion mode: check-on-pass
|
||||||
|
|
||||||
|
## 이 파일을 읽는 리뷰 에이전트에게
|
||||||
|
|
||||||
|
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||||
|
|
||||||
|
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 구현 항목별 완료 여부
|
||||||
|
|
||||||
|
| 항목 | 완료 여부 |
|
||||||
|
|------|---------|
|
||||||
|
| [WEB-RUNTIME-1] Predecessor gate | [ ] |
|
||||||
|
| [WEB-RUNTIME-2] Ignored remote preview environment contract | [ ] |
|
||||||
|
| [WEB-RUNTIME-3] README smoke procedure 연결 | [ ] |
|
||||||
|
| [WEB-RUNTIME-4] Remote server reachability and user evidence handoff | [ ] |
|
||||||
|
|
||||||
|
## 구현 체크리스트
|
||||||
|
|
||||||
|
- [ ] `01_web_scaffold` predecessor complete log를 확인한다.
|
||||||
|
- [ ] remote preview 실행에 필요한 ignored environment variable contract를 `agent-test/local/runtime-env.md` 또는 별도 ignored profile에 정리한다.
|
||||||
|
- [ ] `apps/flutter-test/README.md`에 web runtime smoke 절차를 환경값 없이 연결한다.
|
||||||
|
- [ ] 원격 preview server reachability를 검증하고 사용자 browser smoke evidence 수집 조건을 기록한다.
|
||||||
|
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||||
|
|
||||||
|
## 코드리뷰 전용 체크리스트
|
||||||
|
|
||||||
|
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||||
|
|
||||||
|
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||||
|
- [ ] 판정과 Required/Suggested/Nit 분류가 서로 일치한다.
|
||||||
|
- [ ] PASS이면 `complete.log`를 작성하고 active task directory archive 준비를 한다.
|
||||||
|
|
||||||
|
## 구현 에이전트 기록
|
||||||
|
|
||||||
|
### 변경 요약
|
||||||
|
|
||||||
|
_구현 에이전트가 실제 변경 요약을 작성한다._
|
||||||
|
|
||||||
|
### 계획 대비 변경 사항
|
||||||
|
|
||||||
|
_계획과 달라진 점이 없으면 `없음`이라고 쓴다._
|
||||||
|
|
||||||
|
### 검증 결과
|
||||||
|
|
||||||
|
_실행한 명령과 실제 stdout/stderr를 붙인다. 출력이 길면 저장 경로와 저장 명령을 기록한다._
|
||||||
|
|
||||||
|
### 남은 위험
|
||||||
|
|
||||||
|
_남은 위험이 없으면 `없음`이라고 쓴다._
|
||||||
|
|
||||||
|
## 사용자 리뷰 요청
|
||||||
|
|
||||||
|
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||||
|
|
||||||
|
- 상태: 없음
|
||||||
|
- 사유 유형: 없음
|
||||||
|
- 결정 필요: 없음
|
||||||
|
- 차단 근거: 없음
|
||||||
|
- 실행한 검증/명령: 없음
|
||||||
|
- 자동 후속 불가 이유: 없음
|
||||||
|
- 재개 조건: 없음
|
||||||
|
|
||||||
|
## 코드리뷰 결과
|
||||||
|
|
||||||
|
_리뷰 에이전트가 판정을 append한다._
|
||||||
|
|
@ -0,0 +1,231 @@
|
||||||
|
<!-- task=m-flutter-web-notification-smoke/02+01_web_runtime plan=0 tag=WEB-RUNTIME -->
|
||||||
|
|
||||||
|
# Implementation Plan - WEB-RUNTIME
|
||||||
|
|
||||||
|
## 이 파일을 읽는 구현 에이전트에게
|
||||||
|
|
||||||
|
`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것이 필수다. 구현 후 검증을 실행하고 실제 출력과 변경 내용을 채운 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경, scope 충돌 때문에 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 쓰고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 최종 archive, `complete.log`, roadmap 반영은 code-review/runtime 책임이다.
|
||||||
|
|
||||||
|
## 배경
|
||||||
|
|
||||||
|
`web-runtime`은 Flutter Web server를 원격 preview surface에 띄우고, 사용자가 forwarded localhost URL에서 browser notification permission/display/click 상태를 확인할 수 있게 만드는 작업이다. 이 작업은 `01_web_scaffold`가 web build 가능한 host를 만든 뒤에만 의미가 있다. 실제 host/port/credential 값은 tracked 파일에 쓰지 않는다.
|
||||||
|
|
||||||
|
## 사용자 리뷰 요청 흐름
|
||||||
|
|
||||||
|
구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 직접 사용자 질문은 금지하며, code-review가 blocker 타당성을 검증하고 필요할 때만 `USER_REVIEW.md`를 만든다. 사용자가 forwarded browser를 열어야 하는 수동 evidence가 마지막 blocker로 남으면 실행한 명령, server 상태, 재개 조건을 기록한다.
|
||||||
|
|
||||||
|
## Roadmap Targets
|
||||||
|
|
||||||
|
- Milestone: `agent-roadmap/phase/messaging-runtime/milestones/flutter-web-notification-smoke.md`
|
||||||
|
- Task ids:
|
||||||
|
- `web-runtime`: 원격 Flutter Web server와 forwarded localhost URL 기반 수동 smoke 절차를 정리한다.
|
||||||
|
- Completion mode: check-on-pass
|
||||||
|
|
||||||
|
## 분석 결과
|
||||||
|
|
||||||
|
### 읽은 파일
|
||||||
|
|
||||||
|
- `agent-roadmap/current.md`
|
||||||
|
- `agent-roadmap/phase/messaging-runtime/PHASE.md`
|
||||||
|
- `agent-roadmap/phase/messaging-runtime/milestones/flutter-web-notification-smoke.md`
|
||||||
|
- `agent-ops/rules/project/domain/client-app/rules.md`
|
||||||
|
- `agent-ops/rules/project/domain/workspace-ops/rules.md`
|
||||||
|
- `agent-test/local/rules.md`
|
||||||
|
- `agent-test/local/client-app-smoke.md`
|
||||||
|
- `agent-test/local/runtime-env.md`
|
||||||
|
- `apps/flutter-test/pubspec.yaml`
|
||||||
|
- `apps/flutter-test/lib/main.dart`
|
||||||
|
- `apps/flutter-test/test/widget_test.dart`
|
||||||
|
- `apps/flutter-test/README.md`
|
||||||
|
|
||||||
|
### 테스트 환경 규칙
|
||||||
|
|
||||||
|
사용자 지시상 local 수용 검증은 사용하지 않는다. `agent-test/local/runtime-env.md`의 standard remote runner를 사용하되, 실제 host/port/credential 값은 ignored environment profile에서 변수로 주입한다. `client-app-smoke`의 widget/unit 검증은 참고하되, browser notification permission/display/click evidence는 원격 preview와 사용자 browser 확인이 필요하다.
|
||||||
|
|
||||||
|
### 테스트 커버리지 공백
|
||||||
|
|
||||||
|
- Existing widget tests cover smoke control presence, unsupported VM display, and opened routing via injected events.
|
||||||
|
- Existing tests do not prove that a remote Flutter Web server is reachable through the preview/forwarded URL.
|
||||||
|
- Existing tests do not prove browser permission prompt, visible browser notification display, or click/open routing in a real browser. This plan records the manual evidence path and blocker handoff.
|
||||||
|
|
||||||
|
### 심볼 참조
|
||||||
|
|
||||||
|
Renamed/removed symbols: none.
|
||||||
|
|
||||||
|
### 분할 판단
|
||||||
|
|
||||||
|
이 plan은 `agent-task/m-flutter-web-notification-smoke/02+01_web_runtime`이며 predecessor index `01`에 의존한다. 구현 전 같은 task group에서 `01_web_scaffold/complete.log`가 active 또는 archive에 있는지 확인해야 한다. 없으면 구현하지 말고 dependency missing으로 보고한다.
|
||||||
|
|
||||||
|
### 범위 결정 근거
|
||||||
|
|
||||||
|
이 계획은 첫 Epic의 `web-runtime` Task만 다룬다. Web scaffold 구현은 `01_web_scaffold`가 완료해야 한다. `packages/messaging_flutter` Chrome test 분리, workspace helper opt-in, Full Web Push, service worker, VAPID, core/push-proxy web target은 제외한다.
|
||||||
|
|
||||||
|
### 빌드 등급
|
||||||
|
|
||||||
|
Build lane/grade: `cloud-G07`. Remote preview process control, forwarded browser evidence, and user-owned permission UI are central success conditions.
|
||||||
|
|
||||||
|
## 의존 관계 및 구현 순서
|
||||||
|
|
||||||
|
1. `agent-task/m-flutter-web-notification-smoke/01_web_scaffold/complete.log` 또는 archive의 같은 predecessor complete log를 확인한다.
|
||||||
|
2. 완료 로그가 없으면 구현을 시작하지 않는다.
|
||||||
|
3. 완료 로그가 있으면 remote preview runbook/evidence 절차를 보강하고 server reachability를 검증한다.
|
||||||
|
|
||||||
|
## 구현 체크리스트
|
||||||
|
|
||||||
|
- [ ] `01_web_scaffold` predecessor complete log를 확인한다.
|
||||||
|
- [ ] remote preview 실행에 필요한 ignored environment variable contract를 `agent-test/local/runtime-env.md` 또는 별도 ignored profile에 정리한다.
|
||||||
|
- [ ] `apps/flutter-test/README.md`에 web runtime smoke 절차를 환경값 없이 연결한다.
|
||||||
|
- [ ] 원격 preview server reachability를 검증하고 사용자 browser smoke evidence 수집 조건을 기록한다.
|
||||||
|
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||||
|
|
||||||
|
### [WEB-RUNTIME-1] Predecessor gate
|
||||||
|
|
||||||
|
#### 문제
|
||||||
|
|
||||||
|
`web-runtime`은 web build 가능한 host가 있어야 실행 가능하다. 현재 subtask name `02+01_web_runtime`은 predecessor `01` 완료를 runtime dependency로 선언한다.
|
||||||
|
|
||||||
|
#### 해결 방법
|
||||||
|
|
||||||
|
구현 시작 전에 active/archive 후보만 확인한다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
find agent-task/m-flutter-web-notification-smoke -maxdepth 2 -path '*/01_*/*' -name complete.log -print
|
||||||
|
find agent-task/archive -path '*/m-flutter-web-notification-smoke/01_*/complete.log' -print
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 수정 파일 및 체크리스트
|
||||||
|
|
||||||
|
- [ ] 이 항목은 파일 수정 없음.
|
||||||
|
|
||||||
|
#### 테스트 작성
|
||||||
|
|
||||||
|
테스트 없음. Dependency gate는 file protocol 확인이다.
|
||||||
|
|
||||||
|
#### 중간 검증
|
||||||
|
|
||||||
|
기대 결과: predecessor complete log가 정확히 하나 확인된다.
|
||||||
|
|
||||||
|
### [WEB-RUNTIME-2] Ignored remote preview environment contract
|
||||||
|
|
||||||
|
#### 문제
|
||||||
|
|
||||||
|
원격 web smoke에는 remote shell, repo path, preview container, container repo path, web server port 같은 환경값이 필요하다. tracked docs/roadmap/task/review log에 원문 값을 쓰면 안 된다.
|
||||||
|
|
||||||
|
#### 해결 방법
|
||||||
|
|
||||||
|
ignored local test environment 문서 또는 ignored env profile에 변수 contract만 정리한다. tracked plan/review에는 변수명만 남긴다.
|
||||||
|
|
||||||
|
필요 변수 후보:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
NEXO_REMOTE_SSH
|
||||||
|
NEXO_REMOTE_REPO
|
||||||
|
NEXO_PREVIEW_CONTAINER
|
||||||
|
NEXO_CONTAINER_REPO
|
||||||
|
NEXO_WEB_HOSTNAME
|
||||||
|
NEXO_CONTAINER_WEB_PORT
|
||||||
|
NEXO_BROWSER_PREVIEW_URL
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 수정 파일 및 체크리스트
|
||||||
|
|
||||||
|
- [ ] `agent-test/local/runtime-env.md` 또는 ignored local profile
|
||||||
|
|
||||||
|
#### 테스트 작성
|
||||||
|
|
||||||
|
테스트 없음. 환경 contract 문서화 작업이다.
|
||||||
|
|
||||||
|
#### 중간 검증
|
||||||
|
|
||||||
|
```sh
|
||||||
|
set -a; source agent-test/local/web-smoke.env; set +a
|
||||||
|
test -n "$NEXO_REMOTE_SSH"
|
||||||
|
test -n "$NEXO_PREVIEW_CONTAINER"
|
||||||
|
test -n "$NEXO_WEB_HOSTNAME"
|
||||||
|
test -n "$NEXO_CONTAINER_WEB_PORT"
|
||||||
|
```
|
||||||
|
|
||||||
|
기대 결과: 필요한 변수들이 비어 있지 않다. 실제 값은 review output에 출력하지 않는다.
|
||||||
|
|
||||||
|
### [WEB-RUNTIME-3] README smoke procedure 연결
|
||||||
|
|
||||||
|
#### 문제
|
||||||
|
|
||||||
|
`apps/flutter-test/README.md:73-76`은 browser preview가 나중에 추가된다고 되어 있고, 실제 remote preview smoke 절차로 이어지지 않는다. 해당 문단은 기존 frontend slot을 언급하지만, 이 plan/review에는 실제 port 값을 반복 기록하지 않는다.
|
||||||
|
|
||||||
|
#### 해결 방법
|
||||||
|
|
||||||
|
README는 공개/사람용 문서이므로 값 원문을 쓰지 않고, ignored environment profile을 통해 remote preview server를 띄운 뒤 forwarded localhost URL에서 확인한다는 절차만 적는다. Browser smoke checklist는 permission request, display status, click/open routing status를 포함해야 한다.
|
||||||
|
|
||||||
|
#### 수정 파일 및 체크리스트
|
||||||
|
|
||||||
|
- [ ] `apps/flutter-test/README.md`
|
||||||
|
|
||||||
|
#### 테스트 작성
|
||||||
|
|
||||||
|
문서 변경은 테스트를 추가하지 않는다.
|
||||||
|
|
||||||
|
#### 중간 검증
|
||||||
|
|
||||||
|
```sh
|
||||||
|
rg --sort path -n "Web|browser|permission|display|click|forwarded|environment" apps/flutter-test/README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
기대 결과: web runtime smoke 절차가 환경값 없이 연결된다.
|
||||||
|
|
||||||
|
### [WEB-RUNTIME-4] Remote server reachability and user evidence handoff
|
||||||
|
|
||||||
|
#### 문제
|
||||||
|
|
||||||
|
Browser notification permission/display/click은 agent-only terminal 검증으로 완료할 수 없다. 사용자가 forwarded browser URL을 열어 확인해야 한다.
|
||||||
|
|
||||||
|
#### 해결 방법
|
||||||
|
|
||||||
|
원격 preview server를 실행하고 reachability를 확인한다. 사용자가 browser를 열어야 하는 단계가 남으면 `사용자 리뷰 요청`에 server 상태, URL 확인 방법의 변수명, 재개 조건을 기록한다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
set -a; source agent-test/local/web-smoke.env; set +a
|
||||||
|
ssh "$NEXO_REMOTE_SSH" "zsh -lc 'docker exec ${NEXO_PREVIEW_CONTAINER} sh -lc \"cd ${NEXO_CONTAINER_REPO}/apps/flutter-test && flutter run -d web-server --web-hostname ${NEXO_WEB_HOSTNAME} --web-port ${NEXO_CONTAINER_WEB_PORT}\"'"
|
||||||
|
```
|
||||||
|
|
||||||
|
별도 shell에서 reachability:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
set -a; source agent-test/local/web-smoke.env; set +a
|
||||||
|
ssh "$NEXO_REMOTE_SSH" "zsh -lc 'curl -fsSI ${NEXO_BROWSER_PREVIEW_URL}'"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 수정 파일 및 체크리스트
|
||||||
|
|
||||||
|
- [ ] `CODE_REVIEW-cloud-G07.md` 검증 결과 또는 사용자 리뷰 요청
|
||||||
|
|
||||||
|
#### 테스트 작성
|
||||||
|
|
||||||
|
자동 테스트 없음. 사용자 browser permission UI가 필요한 manual smoke다.
|
||||||
|
|
||||||
|
#### 중간 검증
|
||||||
|
|
||||||
|
기대 결과: server가 응답한다. 사용자가 forwarded browser에서 permission request, notification display, click/open routing 상태를 확인하면 review stub에 결과를 기록한다. 사용자가 열어야 해서 agent가 끝낼 수 없으면 `사용자 리뷰 요청`으로 남긴다.
|
||||||
|
|
||||||
|
## 수정 파일 요약
|
||||||
|
|
||||||
|
| 파일 | 항목 |
|
||||||
|
| --- | --- |
|
||||||
|
| `agent-test/local/runtime-env.md` 또는 ignored local profile | WEB-RUNTIME-2 |
|
||||||
|
| `apps/flutter-test/README.md` | WEB-RUNTIME-3 |
|
||||||
|
| `agent-task/m-flutter-web-notification-smoke/02+01_web_runtime/CODE_REVIEW-cloud-G07.md` | WEB-RUNTIME-4 |
|
||||||
|
|
||||||
|
## 최종 검증
|
||||||
|
|
||||||
|
아래 명령은 ignored environment profile에서 필요한 값을 주입한 뒤 실행한다. 실제 값은 tracked 파일에 기록하지 않는다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
find agent-task/m-flutter-web-notification-smoke -maxdepth 2 -path '*/01_*/*' -name complete.log -print
|
||||||
|
set -a; source agent-test/local/web-smoke.env; set +a
|
||||||
|
ssh "$NEXO_REMOTE_SSH" "zsh -lc 'docker exec ${NEXO_PREVIEW_CONTAINER} sh -lc \"cd ${NEXO_CONTAINER_REPO}/apps/flutter-test && flutter build web\"'"
|
||||||
|
ssh "$NEXO_REMOTE_SSH" "zsh -lc 'curl -fsSI ${NEXO_BROWSER_PREVIEW_URL}'"
|
||||||
|
```
|
||||||
|
|
||||||
|
기대 결과: predecessor complete log 확인, remote/container web build PASS, preview URL HEAD 응답 PASS. Browser permission/display/click은 사용자 forwarded browser evidence가 필요하며, 완료하지 못하면 review stub의 `사용자 리뷰 요청`에 재개 조건을 기록한다.
|
||||||
|
|
||||||
|
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||||
30
apps/flutter-test/.metadata
Normal file
30
apps/flutter-test/.metadata
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: "2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa"
|
||||||
|
channel: "stable"
|
||||||
|
|
||||||
|
project_type: app
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
|
||||||
|
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
|
||||||
|
- platform: web
|
||||||
|
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
|
||||||
|
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||||
|
|
@ -54,6 +54,7 @@ apps/flutter-test/
|
||||||
android/ # Android test host used to load the plugin
|
android/ # Android test host used to load the plugin
|
||||||
ios/ # iOS host scaffold
|
ios/ # iOS host scaffold
|
||||||
macos/ # macOS host scaffold
|
macos/ # macOS host scaffold
|
||||||
|
web/ # Web test host scaffold for browser smoke checks
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running The App
|
## Running The App
|
||||||
|
|
@ -70,10 +71,9 @@ Run on a connected device or emulator:
|
||||||
flutter run
|
flutter run
|
||||||
```
|
```
|
||||||
|
|
||||||
This harness does not own a default host HTTP port. Android/iOS/device smoke
|
This harness does not own default host, port, credential, or forwarding values.
|
||||||
tests should receive runtime URLs from the invoking shell or ignored local
|
Android/iOS/device smoke tests and browser previews should receive runtime URLs
|
||||||
profiles. If a browser preview is added later, use the workspace frontend slot
|
from the invoking shell or ignored local profiles.
|
||||||
`13040` unless a later migration plan changes the baseline.
|
|
||||||
|
|
||||||
For Android FCM smoke testing, make sure the Android app has the required
|
For Android FCM smoke testing, make sure the Android app has the required
|
||||||
Firebase configuration, including `android/app/google-services.json`.
|
Firebase configuration, including `android/app/google-services.json`.
|
||||||
|
|
@ -117,11 +117,14 @@ Run checks from this directory:
|
||||||
flutter analyze
|
flutter analyze
|
||||||
flutter test
|
flutter test
|
||||||
flutter test integration_test
|
flutter test integration_test
|
||||||
|
flutter build web
|
||||||
```
|
```
|
||||||
|
|
||||||
Use `flutter test` for the thin-host contract and `flutter test
|
Use `flutter test` for the thin-host contract and `flutter test
|
||||||
integration_test` for real Flutter host registration, method-channel,
|
integration_test` for real Flutter host registration, method-channel,
|
||||||
event-channel, and opened-routing coverage.
|
event-channel, and opened-routing coverage. Use `flutter build web` to verify
|
||||||
|
that the web host scaffold remains buildable before browser notification smoke
|
||||||
|
checks run on the remote runner.
|
||||||
|
|
||||||
Run Android native unit tests from `apps/flutter-test/android`:
|
Run Android native unit tests from `apps/flutter-test/android`:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,19 @@ import 'package:nexo_messaging/nexo_messaging.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Firebase.initializeApp();
|
await _initializeFirebaseForHarness();
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _initializeFirebaseForHarness() async {
|
||||||
|
try {
|
||||||
|
await Firebase.initializeApp();
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
debugPrint('[NexoClient] Firebase initialization skipped: $error');
|
||||||
|
debugPrintStack(stackTrace: stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MyApp extends StatefulWidget {
|
class MyApp extends StatefulWidget {
|
||||||
const MyApp({super.key, this.initializePlugin = true});
|
const MyApp({super.key, this.initializePlugin = true});
|
||||||
|
|
||||||
|
|
@ -173,8 +182,7 @@ class _MyAppState extends State<MyApp> {
|
||||||
key: const Key('web-display-status'),
|
key: const Key('web-display-status'),
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () =>
|
onPressed: () => unawaited(_refreshWebNotificationStatus(plugin)),
|
||||||
unawaited(_refreshWebNotificationStatus(plugin)),
|
|
||||||
child: const Text('Refresh web permission'),
|
child: const Text('Refresh web permission'),
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
|
|
|
||||||
BIN
apps/flutter-test/web/favicon.png
Normal file
BIN
apps/flutter-test/web/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 917 B |
BIN
apps/flutter-test/web/icons/Icon-192.png
Normal file
BIN
apps/flutter-test/web/icons/Icon-192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
apps/flutter-test/web/icons/Icon-512.png
Normal file
BIN
apps/flutter-test/web/icons/Icon-512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
BIN
apps/flutter-test/web/icons/Icon-maskable-192.png
Normal file
BIN
apps/flutter-test/web/icons/Icon-maskable-192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
BIN
apps/flutter-test/web/icons/Icon-maskable-512.png
Normal file
BIN
apps/flutter-test/web/icons/Icon-maskable-512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
46
apps/flutter-test/web/index.html
Normal file
46
apps/flutter-test/web/index.html
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!--
|
||||||
|
If you are serving your web app in a path other than the root, change the
|
||||||
|
href value below to reflect the base path you are serving from.
|
||||||
|
|
||||||
|
The path provided below has to start and end with a slash "/" in order for
|
||||||
|
it to work correctly.
|
||||||
|
|
||||||
|
For more details:
|
||||||
|
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||||
|
|
||||||
|
This is a placeholder for base href that will be replaced by the value of
|
||||||
|
the `--base-href` argument provided to `flutter build`.
|
||||||
|
-->
|
||||||
|
<base href="$FLUTTER_BASE_HREF">
|
||||||
|
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||||
|
<meta name="description" content="A new Flutter project.">
|
||||||
|
|
||||||
|
<!-- iOS meta tags & icons -->
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
|
<meta name="apple-mobile-web-app-title" content="nexo_client">
|
||||||
|
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||||
|
|
||||||
|
<title>nexo_client</title>
|
||||||
|
<link rel="manifest" href="manifest.json">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!--
|
||||||
|
You can customize the "flutter_bootstrap.js" script.
|
||||||
|
This is useful to provide a custom configuration to the Flutter loader
|
||||||
|
or to give the user feedback during the initialization process.
|
||||||
|
|
||||||
|
For more details:
|
||||||
|
* https://docs.flutter.dev/platform-integration/web/initialization
|
||||||
|
-->
|
||||||
|
<script src="flutter_bootstrap.js" async></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
35
apps/flutter-test/web/manifest.json
Normal file
35
apps/flutter-test/web/manifest.json
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "nexo_client",
|
||||||
|
"short_name": "nexo_client",
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#0175C2",
|
||||||
|
"theme_color": "#0175C2",
|
||||||
|
"description": "A new Flutter project.",
|
||||||
|
"orientation": "portrait-primary",
|
||||||
|
"prefer_related_applications": false,
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "icons/Icon-192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icons/Icon-512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icons/Icon-maskable-192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icons/Icon-maskable-512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue