# TourApp Full E2E Demo Checkpoint — 2026-10-01

> Resume update: the original isolated-DB provisioning blocker is resolved.
> Schema/user are provisioned and verified; the test Backend passed health and
> bootstrap-Admin login on 8788, then was stopped at the owner's request. The
> three-user Full E2E has not started. See the addendum at the end; it supersedes
> the older provisioning-blocked status below.

## Executive status

**PARTIAL.** The original isolated-database provisioning blocker is resolved by
the addendum below. Full live E2E is paused after infrastructure verification
and bootstrap Admin login; no DEMO member, agency, group, bid, payment, or review
has been created. The main `tourist_grouping_db` was not used for test writes.
Earlier Flutter fixture tests remain fixture-only evidence.

## Checkpoint 1 — environment and safe test database

| Check | Result | Evidence |
|---|---|---|
| MariaDB | PARTIAL/PASS running | One listener on 3306 owned by one `mysqld`; main Backend health reports `databaseConfigured=true`. `mysqladmin ping` could not authenticate without a DB password; it did not establish a DB outage. |
| Backend | PASS available | `GET http://127.0.0.1:8787/api/health`: `ok=true`, `databaseConfigured=true`. No restart performed. |
| Agency Portal | PASS public shell | `GET http://127.0.0.1:4173/agency/login`: HTTP 200. Protected Agency/Admin session not tested. |
| Separate E2E Backend | BLOCKED | Nothing listening on 8788. No `TRAVELIN_*TEST_DB*` variables are configured in this test shell. |
| Isolated DB/user | BLOCKED | The existing E-Bidding checkpoint (`audit/ebidding_demo_isolated_database_checkpoint_20260927.md`) records no provisioned test schema/app user and no authorized DBA/phpMyAdmin session. Main app health does not prove CREATE DATABASE/USER grants. Do not try provisioning through the main app account. |
| Android Emulator | PASS online | `emulator-5554`, Android 17/API 37. ADB was found under the Android SDK even though it is not in PATH. |

The launcher and MariaDB startup were not invoked: MariaDB and the primary
services already run, and starting the launcher risks duplicating the selected
Persistent Console process. No process or DB configuration was changed.

### One-time DBA action needed

An authorized local MariaDB operator must first check for name collisions. If
either name already exists, stop and inspect instead of reusing it. Then create
an empty schema and an app account scoped only to that schema, using a generated
local-only password entered outside Git/checkpoints/logs:

```sql
SELECT SCHEMA_NAME FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME = 'tourapp_full_e2e_demo_20261001';
SELECT User, Host FROM mysql.user
WHERE User = 'tourapp_full_e2e_demo_app' AND Host = '127.0.0.1';

-- Run only if both checks returned no rows:
CREATE DATABASE `tourapp_full_e2e_demo_20261001`
  CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'tourapp_full_e2e_demo_app'@'127.0.0.1'
  IDENTIFIED BY '<generate and enter a unique local secret here>';
GRANT ALL PRIVILEGES ON `tourapp_full_e2e_demo_20261001`.*
  TO 'tourapp_full_e2e_demo_app'@'127.0.0.1';
SHOW GRANTS FOR 'tourapp_full_e2e_demo_app'@'127.0.0.1';
```

The operator needs CREATE DATABASE, CREATE USER, and grant authority for the
schema-scoped grant. Do not grant `*.*`, and do not import the full application
dump: it contains business rows. After provision, the password and all `DB_*`
values must be supplied only to a test Backend process on port 8788. The E2E
runner must assert the exact test schema name before any write. If desired, the
operator may reuse the older proposed `tourapp_ebidding_demo_20260927` only after
checking it is absent and empty; this run did not verify that DB catalog.

## Checkpoint 2 — new users, AI and Matching

| Flow step | Result | Method |
|---|---|---|
| Register/login three fresh DEMO users | BLOCKED | Requires the absent isolated schema/test Backend. No credentials were guessed or created. |
| Gemini analysis on these users | BLOCKED | No DEMO preferences were submitted. Prior checkpoint evidence of a standalone Gemini API success is historical evidence only, not this end-to-end flow. |
| Matching V2 across three persisted preferences | BLOCKED | No real preferences/users were inserted and the main DB was not used. |
| Matching screen and automatic route | PASS, fixture only | `flutter test integration_test/automated_matching_flow_test.dart -d emulator-5554`: 1/1 passed on Android Emulator. `MockClient`; no real Backend/DB request. |

## Checkpoint 3 — chat and Destination Poll

| Flow step | Result | Method |
|---|---|---|
| New DEMO group chat, member access and saved chat | BLOCKED | No group exists in the isolated DB. |
| City then place voting and group result | PASS, fixture only | `flutter test integration_test/destination_poll_japan_test.dart -d emulator-5554`: 1/1 passed on Android Emulator. `MockClient`; fixture city/place data only. |
| Poll/Chat aggregate on a real DEMO group | BLOCKED | Requires group from Matching in the isolated DB. |

Together, the two newly run emulator tests were **2/2 PASS for Flutter UI with
mock HTTP**, not a live connected E2E. No screenshot files were captured in this
run. Historical China poll evidence remains in prior checkpoints and was not
relabelled as a live test for this new flow.

## Checkpoint 4 — Agency bidding and voting

**BLOCKED.** No test Agencies authenticated, no protected Portal pages tested,
and no bid/vote records created. Prior Portal HTTP 200 proves only the public
login shell; the September checkpoint records no authorized Agency/Admin
credentials. Do not use the main Portal or guess credentials.

## Checkpoints 5–8 — payment, Trip Hub and completion

**BLOCKED before payment setup.** No E2E payment record or test file was
submitted. No SlipOK API request was sent in this run; no payment provider
status changed. The previous replacement slip was already sent once with
`log=true` in the earlier SlipOK diagnostic, so this run did not resend it.

The attached E2E request authorizes `log=false` only for a local isolated test
Backend. That configuration was not changed because there is no isolated
Backend/schema to constrain it. The production/default adapter remains
`log=true`. Contract distinctions:

- A future `LIVE SLIPOK CHECK (log=false)` is only a provider QR/amount/reference
  diagnostic; it does not verify branch receiver or provider-side duplicate
  history. Do not mark that as a successful real payment.
- To complete a DEMO trip for multiple test users, use the explicit development
  Demo verifier in the isolated DB and label it as mock. Do not reuse a real
  transaction reference across DEMO payments or weaken the production unique
  constraints.
- The real app currently stores payment amount from the selected winning bid's
  `price` (`_validatePayableBid`); the evidence upload amount is loaded from that
  persisted payment. A 1.00 DEMO payment therefore needs a test winning bid
  priced at 1.00 per person, if accepted by the existing positive-price
  validation. This is not a group-total calculation.

No Trip Hub/journey/rating status was simulated, and no review was created.

## Test evidence and reuse

- New on-device Flutter integration tests: 2 passed (matching route and Japan
  city/place poll), both fixture/MockClient.
- No new server integration or database test was run, because no isolated DB
  credentials are available. No main-DB test was substituted.
- The previously reported targeted SlipOK suite (64/64) is reused as prior
  adapter evidence; it is not evidence of this Full E2E.
- No full project analyzer/build suite was repeated; this run changed no source
  code.

## Demo inventory

No DEMO records were created. Users, Group ID, Poll IDs, Agency IDs, Bid IDs,
Payment IDs, Trip status and Review IDs: **none**. No new screenshots.

## Data safety

This run made read-only health/port checks and ran emulator tests against
`MockClient`. It did not issue SQL, call protected write APIs, submit a slip,
change the main Backend/MariaDB, or write records/files in `tourist_grouping_db`.
Groups #115/#102 and their voting/payment/bid/history were not modified. Recovery
datadirs/backups were not accessed. No old Payment or DEMO data was deleted.

## Resume point

After the owner/operator provisions the empty schema and restricted DB user,
continue here: apply schema-only DDL/current migrations and only required
reference catalog; start Backend 8788 with explicit test-only DB config; verify
health and exact DB identity; then create fresh users through Register/Login and
continue the UI/API flow. Never point Flutter/Portal test sessions to 8787 for
write operations. Stop if the test Backend cannot prove it is connected only to
`tourapp_full_e2e_demo_20261001`.

## Addendum — 2026-10-01: infrastructure provisioned; workflow paused

### Isolated database and server

- Confirmed local XAMPP MariaDB 10.4.32 and configured DBA identity `root@localhost` with sufficient grants. No existing password or grant was changed.
- Collision checks were clear. Created only `tourapp_full_e2e_demo_20261001` (utf8mb4) and `tourapp_full_e2e_demo_app`@`127.0.0.1`, with privileges restricted to that schema. A read against `tourist_grouping_db.users` using the new account was denied.
- Imported a no-data snapshot of the current migrated schema: 33 tables, no INSERT/UPDATE/DELETE/DROP/TRUNCATE statements and no application rows. The import connected as the scoped test account directly to the E2E schema.
- Added only reference data in the E2E schema: nine UTF-8 `travel_categories` from the existing migration and three Japan Destination Poll catalog rows (`Tokyo`, `Kyoto`, `Osaka`) using existing asset paths and project catalog names/highlights. No package-tour, member, agency, offer, review, or payment data was imported.
- DB credential is stored DPAPI-protected for the current Windows user at `%LOCALAPPDATA%\TourAppE2E\test-db-credential.xml`; no plaintext credential is in the project/checkpoint.
- Test Backend used all five explicit `DB_*` values for this test schema/account, port 8788, development mode, and the Demo slip verifier. Its working directory/private uploads are isolated under `%LOCALAPPDATA%\TourAppE2E\runtime`. Gemini was configured; no Gemini request was made.

### Verification completed

| Check | Result |
|---|---|
| E2E backend `/api/health` | HTTP 200, `ok=true`, database configured |
| Test DB identity | `tourapp_full_e2e_demo_20261001` / scoped test account |
| Initial app-data counts | users 0, groups 0, agencies 0, bids 0, payments 0; admins 1 (bootstrap only); categories 9; Poll catalog 3 |
| Bootstrap Admin login | PASS, Admin ID 26; credential DPAPI-protected outside project at `%LOCALAPPDATA%\TourAppE2E\admin-credential.xml` |
| Processes after pause | MariaDB 3306, main Backend 8787, main Portal 4173 still listening; test Backend 8788 stopped cleanly |

No member/agency/group/preference/poll/chat/bid/payment/trip/review or document
was created. Portal 4174 and Flutter on 8788 were not started. No SlipOK call or
payment verification was made; the old physical slip was not resent. Main DB
was only read for a point-in-time baseline: group 115 `confirmed`/2 members/2
bids; group 102 `open`/0 members/1 bid; bid 39 and payment 2 each existed. No
write touched `tourist_grouping_db`, groups #115/#102, their histories, or MariaDB
recovery folders.

An initial Windows-console seed attempt replaced Thai characters with `?` in
six test-only category rows. The exact rows were verified as self-created, with
no category references, then replaced from the migration's UTF-8 source using
UTF-8 hex literals. Final state verified nine categories, three Japan catalog
rows, and no category names containing `?`. No source file or main data changed.

### Resume instructions

1. Rehydrate DPAPI credentials only in memory as the same Windows user. Start only the test Backend on 8788 with explicit test DB values; the in-memory Admin session expired when it stopped, so log in again. Do not use `start_tourapp.bat` for test writes (it targets the main stack).
2. Keep main Portal 4173 unchanged; run a separate Portal on 4174 with `VITE_API_BASE_URL=http://127.0.0.1:8788`. For Flutter use `adb reverse tcp:8788 tcp:8788` and a temporary launch with `TRAVELIN_API_BASE_URL=http://127.0.0.1:8788`.
3. Continue with three new registrations through the real API/UI, Gemini analysis, Automatic Matching, then Poll/chat, test Agency registration and Admin approval, two bids, voting/finalization, Demo payment, journey completion, and rating. Do not rerun the earlier MockClient tests.
4. Any license attachment must be explicitly DEMO/non-official in the isolated runtime; approval must not be presented as real license verification. Backend was configured to use Demo verifier; no real provider request occurred. Do not resend the physical slip or claim a real payment.

## Addendum — 2026-10-02: isolated live-API E2E completed through rating

This addendum supersedes the earlier “workflow paused/no DEMO records” status. The E2E was executed against the isolated test schema through the real local Backend API. It was not a full UI E2E.

### Environment and boundaries

- MariaDB remained the existing XAMPP MariaDB instance on 3306; it was not restarted. The test Backend on 8788 connected as `tourapp_full_e2e_demo_app@127.0.0.1`; `DATABASE()` returned only `tourapp_full_e2e_demo_20261001`. The app account had previously been confirmed unable to read the main `tourist_grouping_db` schema.
- Test Portal was launched separately on 4174 with API base 8788. The public Agency login shell returned HTTP 200 and was visually inspected in the in-app Browser. No authenticated Portal page was verified in the browser.
- Main Backend 8787 and main Portal 4173 were not listening at the final readiness check; they were not restarted or changed as part of this isolated E2E. Test Backend 8788 and test Portal 4174 were listening.
- `adb devices -l` returned no devices. Flutter UI/Emulator verification is BLOCKED; no Flutter UI pass is claimed.
- Test Backend was restarted only on 8788 with the development Demo slip verifier explicitly selected and SlipOK configuration empty. No real SlipOK/payment-provider request was made.
- E2E credentials/sessions remain DPAPI-protected under `%LOCALAPPDATA%\TourAppE2E`; no password, token, key, or private upload contents were copied into this checkpoint.

### Flow result — real local API and isolated MariaDB

| Step | Result and evidence |
|---|---|
| Register/login | PASS through real API for three new DEMO members (IDs 1139–1141); API sessions were used for subsequent calls. This was not Flutter UI registration/login. |
| Gemini preference analysis | PASS: three real Backend `/api/analyze` requests returned HTTP 200; Japanese destination and preference data were then submitted through the API. No credentials or prompt payloads are recorded here. |
| Automatic Matching | PASS: three persisted preferences formed the same new automatic group, ID 116; readback reported 3/3 members. |
| Chat | PASS: a DEMO message was sent and read in group 116 through the API. |
| Destination Poll | PASS: region/city poll 51 finalized with Tokyo 2, Kyoto 1, Osaka 0; place/activity poll 52 then offered Tokyo-only choices and finalized with Shibuya 2, Asakusa 1, Shinjuku 0. All three group members voted at each stage. |
| Agency setup | PASS in the isolated DB: Admin test account approved DEMO-only Agencies A/B (IDs 42/43); both Agency accounts logged in through the API. Approval was test setup, not real license verification. |
| Bids and isolation | PASS: A submitted bid 50 at THB 1.00/person; B submitted bid 51 at THB 2.00/person, each with a private DEMO program PDF. User offers returned both bids. Agency A’s attempt to edit B’s bid was denied HTTP 403. |
| Agency voting | PASS: all three members voted. One member changed their vote before close. Finalize selected bid 50/Agency A with three votes; repeating finalize returned the already-finalized result. |
| Payment | PASS for the development Demo verifier only: payment 18, user 1139, winning bid 50, THB 1.00, status `paid`, provider `demo`. A payment for non-winning bid 51 was denied HTTP 403. This is synthetic test state, not a real payment or SlipOK verification. Only one of three members has a payment record. |
| Journey and rating | PASS through API: Agency A updated DEMO meeting details and transitioned the journey through preparing → ready → traveling → completed. Member 1139 recorded a DEMO review (trip 5/5, agency 5/5). Other two members have no payment/review records. |

The Japanese test catalog rows initially had malformed test-only JSON. Only those three rows in the isolated test schema were corrected from the project’s existing Japanese catalog values; after correction, the original lazy poll creation API produced the expected city-scoped choices. No project source or main database row was edited.

### Security and consistency checks

- Anonymous group-state request: HTTP 401. Agency token used on the user group-state endpoint: HTTP 401.
- Cross-user notification mark-read attempt: HTTP 403; the target user’s notification remained unread.
- Agency B’s journey list did not include Agency A’s group 116 trip.
- Direct checks in the test schema found 0 orphan memberships, 0 duplicate memberships, 0 group member-count drift, 0 user/group pointer drift, 0 payments on a non-winning bid, 0 review ownership/winner mismatches, 0 journey/winner mismatches, and 0 duplicated non-empty notification dedupe keys.
- Final test-schema counts: users 3; agencies 2; groups 1; memberships 3; preferences 3; bids 2; bid votes 3; group voting rounds 1; destination polls/rounds/votes 2/2/6; payments 1; notifications 32; travel-document submissions 0; journey details 1; reviews 1; chat messages 1. The two bids each retained one private DEMO program document. No travel-document submission flow was part of this E2E.

### Overall status and resume point

- **Live API E2E on isolated MariaDB test schema:** PASS through one member’s Demo payment, completed journey, and rating.
- **Authenticated Agency/Admin browser UI:** PARTIAL/BLOCKED; only the real test Portal public login page was visually inspected. API authentication, role checks, bids, and journey access were exercised directly.
- **Flutter Emulator UI:** BLOCKED because ADB reported no online device.
- **Payment:** Demo verifier only; do not describe this as a real transfer, SlipOK verification, or paid transaction outside this test DB.
- **Regression/analyzer suites:** not rerun in this continuation because no source code changed; prior MockClient suite was not rerun or relabelled as live E2E.
- **Screenshots:** no screenshot files were saved.
- **Source changes:** none. This audit checkpoint is the only project file updated. DEMO rows/files are retained only under the isolated E2E schema/runtime for later demonstration; no cleanup was run.
- **Protected data:** this run wrote only to `tourapp_full_e2e_demo_20261001` and its isolated upload folders. It did not modify `tourist_grouping_db`, group #115, group #102, payment history, MariaDB datadir, or recovery backups.

To resume the demo without creating records again, use the existing MariaDB console, test Backend 8788 and Portal 4174 (not the main launcher, which targets the main stack), and the DPAPI-protected credentials under `%LOCALAPPDATA%\TourAppE2E` in the same Windows account. Flutter demo verification still requires bringing an Emulator online and launching Flutter with the temporary 8788 API base; do not change the main app/backend config.

## Addendum — 2026-10-02: real-device UI readiness and handoff

This update is limited to emulator/UI readiness and per-member state inspection; the previous live API E2E was not repeated.

### Emulator and app

- Android SDK tools exist under the configured local SDK. The existing `Pixel_6` AVD is available; an already-running device was reused (`emulator-5554`, reported model GM1910, Android 14/API 34, boot complete). No duplicate emulator was launched.
- Added only `adb reverse tcp:8788 tcp:8788`; the reverse list showed 8788 and did not map 8787.
- Launched the existing Flutter app with `flutter run --no-pub -d emulator-5554 --dart-define=TRAVELIN_API_BASE_URL=http://127.0.0.1:8788`. Debug build/install/launch succeeded and the app displayed the Guest/Home screen.
- From the emulator, a read-only request to `http://127.0.0.1:8788/api/health` through the reverse mapping returned `ok=true`, `databaseConfigured=true`, `aiConfigured=true`. This verifies the emulator-to-test-backend route and the app was compiled with that same test base URL. No authenticated Flutter API call was triggered.
- Actual Flutter screenshot: `audit/ui_e2e_screenshots_20261002/flutter_app_start.png` (Guest/Home screen). No screenshot was fabricated.

### Agency Portal Browser

- The Browser remained on `http://127.0.0.1:4174/agency/login`; the real test Portal login screen rendered. Authenticated pages were not tested in Browser.
- Computer Use policy prohibits automating authentication dialogs. The Agency login needs to be completed manually by the owner in the open test Portal tab; no password was entered or exposed by automation. Backend/API Agency login and authorization evidence from the earlier addendum remains separate evidence, not a browser UI pass.

### Member payment and rating state (read-only)

| DEMO member | Payment for winning bid 50 | Journey status | Can review | Existing review |
|---|---|---|---|---|
| 1139 | `paid` via `demo` (payment 18) | `completed` | yes | yes |
| 1140 | `not_started` | `waiting_payment` | no | no |
| 1141 | `not_started` | `waiting_payment` | no | no |

No Payment or Review record was added or changed in this UI-readiness pass. Group 116 is already marked completed, while two members have no payment. Adding their payments now would exercise a post-completion payment sequence rather than the intended order, so the request to test Demo Payment and Rating for all three is **BLOCKED for group 116**. Its existing review was preserved. For a correct all-member sequence, create a fresh DEMO trip in the isolated schema through the normal lifecycle; do not reset or roll back group 116.

### UI E2E result and next action

- **Emulator startup / Flutter app launch:** PASS.
- **Emulator network route to Backend 8788:** PASS (device-side HTTP health request); **Flutter authenticated feature request:** not yet verified.
- **Flutter UI beyond Guest/Home:** BLOCKED pending owner-operated login; no Matching/Poll/Bidding/Voting/Payment/Trip/Rating actions were tapped in this pass.
- **Agency Portal public login UI:** PASS; **Agency Portal post-login UI:** BLOCKED pending owner-operated login.
- **Portal and Flutter screenshots:** only the Flutter Guest/Home screenshot was saved. No screenshot is claimed for authenticated pages.
- No source/business-data changes were made. The only new artifact is the screenshot and this checkpoint update. No API E2E was rerun; health/payment-status/journey-status and DB checks were read-only.
- Main DB, groups #115/#102, Payment history, MariaDB datadir, and recovery backups were not touched. Test Backend 8788, Portal 4174, Flutter app, and the 8788 reverse mapping are left available for continuation.

Handoff: the owner must sign in manually to the Agency Portal and Flutter app using DEMO credentials stored DPAPI-protected under `%LOCALAPPDATA%\TourAppE2E`. Once the authenticated screen is open, continue UI observation from that state; keep Flutter pointed at 8788 and do not use the main launcher for DEMO writes.

## Addendum — 2026-10-02: instrumented Flutter Login attempt pending

This is a focused continuation of the Login-hang investigation only. No Full E2E, Matching, or business-data tests were repeated.

### Environment and route evidence

- Test Backend 8788 is still owned by the existing Dart process (PID 11136, started 2026-10-02 09:56 local); it was not restarted. The prior checkpoint recorded this running test process as connected only to `tourapp_full_e2e_demo_20261001`. No MariaDB process or configuration was changed.
- The Flutter debug app was rebuilt with the explicit compile-time API base `http://127.0.0.1:8788`; ADB showed `tcp:8787` reverse only and no 8788 mapping. To prevent any route to the main backend and make the configured test route usable, the 8787 reverse was removed and `tcp:8788` reverse was added. Current mapping contains only 8788.
- The app/API source confirms both the Saved Account Tile and regular login form call the same `_authenticate` → `AuthService.login` flow. The immediate Home refresh chooses `/api/groups/state` for a joined member or `/api/matching/status` otherwise.

### Temporary diagnostics and verification

- Debug-only, secret-safe `[LOGIN_DIAG]` markers were added temporarily to `lib/pages/login_page.dart`, `lib/services/auth_service.dart`, and `lib/pages/home_page.dart` for tap, request target, HTTP status/timing, parsed response shape, session save, first post-login API, navigation, and loading completion. They do not print request/response bodies, identifiers, passwords, tokens, or exception text.
- Targeted analyzer check passed: `dart analyze lib/services/auth_service.dart lib/pages/login_page.dart lib/pages/home_page.dart` — no issues found.
- Flutter debug build/install on `emulator-5554` succeeded. App data was not cleared. Backend 8788 and MariaDB were not restarted.
- No `[LOGIN_DIAG]` marker appeared after the build, so no post-instrumentation Login tap has yet been observed. Consequently no Login HTTP status/duration, post-login API result, or confirmed root cause is available yet. No regression test or functional fix has been added pending that evidence.

### Resume point

The owner should perform exactly one manual Login attempt in the open Flutter app and reply that it was tapped. Read only `[LOGIN_DIAG]` markers from Android logcat; if a response is observed, inspect only the corresponding status, duration, parse-shape booleans, and post-login endpoint outcome. Do not automate credential entry. The temporary debug markers should be removed after diagnosis/fix; do not proceed to UI E2E until Login is verified.

No test schema data, groups #115/#102/#116, main Backend 8787, main database, MariaDB datadir, or recovery backups were modified.

## Addendum — 2026-10-02: Login result and test-account mismatch

The owner-operated Flutter attempt was inspected from diagnostic-only Android log markers; no credential or response body was read.

- The latest captured request was `POST /api/auth/login` to `127.0.0.1:8788`, and it returned HTTP 401 in 236 ms. Other submissions in the captured interval also returned 401 promptly.
- The active test Backend listener remained PID 11136. ADB reverse was corrected before this attempt to map only `tcp:8788` (the stale `tcp:8787` mapping was removed). The response therefore came through the isolated test route, not the main Backend.
- Read-only MariaDB verification using the protected test DB credential confirmed `DATABASE()` was `tourapp_full_e2e_demo_20261001`. The account shown by the failed recent-account flow (saved-account slot 1) does not exist in that schema. None of the four built-in Flutter saved-account entries exists there. The three Full API E2E member rows (IDs 1139–1141) are present and none matches those saved entries.
- This is an account/environment mismatch, not a slow Login or post-login API: no session was saved, no post-login `/api/groups/state` or `/api/matching/status` call occurred, navigation did not occur, and `LOADING_FALSE mounted=true` was recorded. Backend auth intentionally returns the same 401 message for an unknown/inactive account and an incorrect password; the read-only account lookup establishes that the selected saved account is absent.
- No password, account email, password hash, token, request body, or private account data was recorded in this checkpoint. The temporary `[LOGIN_DIAG]` source instrumentation was removed after evidence capture. Targeted `dart analyze` passed; no functional source fix or regression test was needed because the rejected account is not present in the test DB.

For the completed DEMO group 116, use one of its existing E2E members (user IDs 1139–1141) with the credentials originally created for that test run; the protected local credential record is `%LOCALAPPDATA%\TourAppE2E\member-credentials.xml`. Do not use the four prefilled recent-account tiles for the isolated test Backend. Alternatively, register a new unique test account through Flutter’s normal registration form; the current app is configured for 8788, but that new user will not automatically belong to group 116.

Flutter UI Login has not yet passed with a valid test account. No API/Matching E2E was repeated, no payment was invoked, no test or main business data was changed, and groups #115/#102/#116 plus MariaDB/recovery data remain untouched.

The installed Emulator debug APK is still the diagnostic build until the next successful debug rebuild; its remaining runtime markers contain only the safe stages/route/status/timing described above. Source instrumentation has been removed. An optional Release build attempt was not usable because the existing generated Android plugin registrant references a missing `integration_test` plugin; no Release APK was installed and no app data was cleared.

### Saved Account continuation — 2026-10-02: initial authentication handoff

- Re-read the Saved Account checkpoint and its changed-file list before continuing. `git status/diff` is unavailable because neither the workspace root nor `travelin_app` has a `.git` directory; no repository was initialized and no prior edits were reverted. The ten implementation/dependency files remain those listed above.
- Pixel_6 (`emulator-5556`) is online. Flutter app process PID 8434 remains installed/running; the existing debug APK was built with `TRAVELIN_API_BASE_URL=http://127.0.0.1:8788`. Test Backend PID 8680 remains running and `/api/health` returns HTTP 200 with `databaseConfigured=true`. No service was restarted.
- A stale device-side ADB reverse mapping to 8787 was found and removed. The emulator now has only `tcp:8788` mapped. No request was routed to 8787 and no main service was modified.
- `%LOCALAPPDATA%\TourAppE2E\member-credentials.xml` exists with three DPAPI-protected credential records. It is a credential file, not a Saved Account session store. Backend member sessions are in-memory and the current 8788 process is newer than the prior test-backend process, so earlier server sessions cannot be assumed valid. No valid bearer token was available to validate/import; no token was read, printed, or fabricated.
- Owner reported there is no Profile page/switch control. This is expected until one normal Login succeeds: the switch control is inside the authenticated Profile page. The current Computer Use inventory exposes no targetable native/emulator UI, so the agent could not independently inspect/tap the Pixel screen. No authentication action was automated.
- The already completed Saved Account verification remains: 13/13 targeted widget tests passed; targeted Dart analysis found no issues; the debug APK built and was installed with `-r`. There has been no live successful Saved Account tile tap on this emulator yet. Saved Accounts 1139/1140/1141 and cache isolation therefore remain **NOT VERIFIED**.
- Safe one-time setup required: use “ใช้บัญชีอื่น” on the Flutter Login page and enter each DEMO credential locally, one account per normal authenticated Login. After the first successful Login, Profile appears; choose “สลับบัญชีที่บันทึก” to return to Login without revoking the saved server session, then repeat normal Login for the next DEMO account. Once all three logins have succeeded, the Login page should list all three environment-scoped accounts, after which the owner can tap 1139 → switch → 1140 → switch → 1141. If a session expires or Backend 8788 restarts, use normal Login again; no auth bypass is provided.
- No Preference was submitted/cancelled, no Waiting state was changed, and no database was written. Main Backend 8787/database, groups #115/#102, MariaDB datadir, and recovery backups remain untouched. The test Backend, app, and test schema were left available.

## Addendum — 2026-10-02: login-hang diagnosis checkpoint

No new group was created and no source, database, or backend configuration was changed in this diagnostic pass.

### Observed state

- The active Flutter app on `emulator-5554` is at its recent-account chooser. One account tile remained in its loading state in two real emulator captures 20 seconds apart. The transient diagnostic captures containing account details were removed after inspection; no user/account identifiers are retained here.
- The Agency Portal at `http://127.0.0.1:4174/agency/login` showed its normal idle login form with no error or loading state. No Portal login submission was observed, so this does not indicate a Portal login failure.
- Test Backend 8788 and test Portal 4174 were listening. The running Flutter tool process was configured with `TRAVELIN_API_BASE_URL=http://127.0.0.1:8788`; ADB reverse included 8788 and did not include 8787.

### Request and log evidence

- Flutter app logcat (PID 3077, 500 most recent lines): no `/api/auth/login` route, HTTP response status, Flutter exception, or network-timeout entry was recorded.
- At the capture interval, the host had no established TCP connection to 8788 (only its listener). The test Backend console produced no new output. Its current implementation does not emit general request/status access logs, so the missing log line cannot prove whether a short request completed earlier.
- Therefore the Login HTTP status/response body and any post-login calls were **not captured**. Health is not treated as a Login result.

### Code-path assessment and result

- Current source sends `POST /api/auth/login` through `ApiConfig.baseUrl`; the HTTP request has a 15-second timeout, and `LoginPage._authenticate` clears the spinner in `finally`.
- The persistent UI spinner conflicts with that source-level timeout/finally behavior, but current evidence does not distinguish a request/runtime stall from the running app or UI state not following the inspected source. No root cause is confirmed; no speculative fix or regression test was added.
- **Flutter Login UI:** FAIL / still loading at observation; API status unknown.
- **Agency Portal Login UI:** not attempted; normal idle form observed.
- **Full UI E2E continuation:** BLOCKED pending a captured Login request/response or Flutter runtime stack. No API E2E was repeated.
- This investigation itself made no DB writes and did not touch the main database, groups #115/#102, MariaDB, or test group #116. A request potentially initiated by the owner before/during observation cannot be attributed from current logs.

Next diagnostic step: obtain a sanitized observation of one owner-operated Login attempt (route, HTTP status, duration only; never request body/token) together with Flutter runtime error/stack capture. If that still shows no request, inspect the active Dart isolate/runtime before changing application code.

## Addendum — 2026-10-02: reset DEMO group 116 for owner-driven Matching UI

### Scope and backup

- Before data access, the active test Backend on port 8788 was confirmed healthy (`databaseConfigured=true`), and a direct connection using the test-scoped DB account returned `DATABASE() = tourapp_full_e2e_demo_20261001`, port 3306. No main Backend or main database connection was used.
- Created a new, non-overwriting logical backup at `database/backups/demo_group_116_reset_25691002_120439/tourapp_full_e2e_demo_20261001.sql` before cleanup. `mysqldump` exited successfully; the file is 73,784 bytes and contains 33 table definitions and 27 insert statements for the test schema, with no `tourist_grouping_db` reference. SHA-256: `460062A891CF0D9EB975DAC0B236855DD0EDF8FDEBD57F718DEFF6ED7870329F`.
- Backup is structurally restore-ready. A scratch restore was not performed: the test account is limited to the existing test schema and has no `CREATE DATABASE`/global write privilege, and no privileged account was used to expand scope.

### Pre-cleanup relationship audit

- Group 116 was an automatic test group with three active members: users 1139, 1140, and 1141. Each had one matched preference and no membership in another group.
- The group owned destination polls 51/52 and six poll votes; bids 50/51 with three agency votes and two DEMO program files; one finalized agency voting round; one Demo-verifier payment; one chat message; one meeting; one completed journey; one review; and 32 group/lifecycle notifications. It had no travel-document submissions/files or Trip Hub acknowledgements.
- All tables were InnoDB. Before cleanup, the membership/group-count, user/group-pointer, and orphan-membership checks were zero. The two Agency accounts and their packages are not group-owned and were intentionally preserved.

### Cleanup and verification

- Removed only group 116 and its scoped lifecycle rows in one InnoDB transaction with foreign-key checks left enabled. The transaction also removed only the three users' preference rows/categories and set their `users.group_id` values to `NULL`; user accounts, password hashes, and profile fields were not targeted. The three matching-owned files (two bid PDFs and one Demo payment evidence file) were deleted by exact path under `%LOCALAPPDATA%/TourAppE2E/runtime` after commit; no directory or unrelated upload was removed.
- Deleted rows: group 1; memberships 3; preferences 3; preference categories 6; polls 2; poll options 12; poll rounds 2; poll-round options 6; poll votes 6; bids 2; bid documents 2; bid votes 3; agency voting rounds 1; Demo payments 1; notifications 32; chat messages 1; journey 1; meeting 1; review 1. No document submissions/files, acknowledgements, or group categories existed. Cleared three user-to-group pointers. Retained Agency accounts 42/43 and their two packages.
- The test database's `matching_runtime_lock` table was empty even though `database/travelin_automated_clustering.sql` seeds row `lock_id=1`. Added that one expected runtime-lock row using the migration's `INSERT IGNORE` seed; no schema or Matching rule changed.
- Post-commit checks in the same verified test schema found zero rows for group 116, its membership/preferences/polls/votes/bids/payments/chat/voting/journey/meeting/review/notification state. Users 1139–1141 remain present and active with `group_id=NULL`; Agency accounts and packages remain present. Global group-member-count drift, user/group-pointer drift, and orphan-membership counts remain zero. Test Backend 8788 health returned HTTP 200 with `databaseConfigured=true`; ADB reverse for 8788 remains present and 8787 is not mapped.
- No new group was created and no Matching request was sent. With no preference row, the current Matching status implementation returns `none`; the owner can now sign in to Flutter on the 8788 test route and enter fresh preferences. Group 116 is no longer available as a demo-history example.

The reset changed only `tourapp_full_e2e_demo_20261001` and three exact test-upload files under its isolated runtime. `tourist_grouping_db`, groups #115/#102, the MariaDB datadir, recovery backups, and main services were not touched. The original Full API E2E results above remain historical evidence only; they are not a retained DEMO group after this reset.

## Runtime checkpoint — 2026-10-02: owner-operated Login and rendering freeze

- The credential file currently exists at `C:\Users\Asus\AppData\Local\TourAppE2E\member-credentials.xml`. The execution identity and file owner are the same Windows user, Asus. Import succeeds under Windows PowerShell 5.1 and decrypts the three DEMO SecureString credentials locally. The earlier owner's DirectoryNotFound error is not reproducible; its historical cause is not proven. No credential was reset or printed.
- Added `tools/show_demo_member_credentials.ps1`: an owner-operated local viewer for the three DEMO accounts, with the password masked until explicitly revealed. It makes no DB/API calls and does not output/persist passwords or display tokens. Script syntax validation passed. Standard Windows PowerShell script execution is Restricted; the existing bundled PowerShell runtime opens the viewer without changing execution policy.
- **Actual Flutter UI Login PASS:** the owner entered a DEMO credential and reported successful login. PID 5250 diagnostics captured `LOGIN_TAP source=credential-form` at 06:56:37 UTC, request host 127.0.0.1 port 8788, HTTP 200 in 580 ms, parsed user/token presence, `LOGIN_SESSION_SAVED`, navigation pop, and `LOGIN_LOADING_FALSE`. The subsequent matching-status requests returned HTTP 200. No request body or credential was recorded.
- Session storage in current AppSession is in-memory; restarting the app process requires signing in again. This checkpoint does not claim persistent login across process restarts.
- **Freeze reproduction:** the owner reported a freeze and later confirmed the screen was black. Both emulator-window capture and Android screencap showed black. The foreground/resumed Android activity was `com.travelin.app`, PID 5250. The actual emulator is LDPlayer14, device emulator-5554.
- Dart VM service and `ext.flutter.debugDumpApp` continued to respond. Main isolate state was Resume, runnable=true; sampled Dart stack had no active frames. Runtime widget names included HomePage and SmartSearchPage, not GroupsPage; no WAITING_PAGE_INIT was logged. This occurrence is therefore not proven to have reached Waiting.
- Read-only VM inspection of WidgetsBinding showed lifecycle resumed, scheduler idle, frames enabled, hasScheduledFrame=true, warmUpFrame=false. The timeline showed a last Frame/Animator::BeginFrame timestamp of 10772599748/10772599760 microseconds, while later secondary-vsync events continued. This supports a frame-delivery/rendering stall, not proof of a Dart infinite loop or a Matching API defect. Native debuggerd backtrace was unavailable without root; no privilege escalation attempted.
- PID 5250 startup explicitly reported Impeller/Vulkan. Warnings alone are not treated as root cause. A controlled comparison was started using the same installed APK with Android launch extra `enable-impeller=false`; only the app process was restarted (PID 5795), with no clear-data/uninstall, DB/service restart, source/business-rule changes, or cache purge. The app rendered Home normally afterward. Waiting verification with this renderer remains pending owner interaction; this is not yet a confirmed fix.
- Immediately before the comparison, read-only matching-status calls to port 8788 using the three stored DEMO sessions returned status none, no preference, and no assigned group. No preference was submitted/cancelled by the agent. ADB reverse maps 8788 only; no 8787 mapping.
- Existing verification retained without rerunning: targeted Dart analysis of the seven diagnostic-touched files had no issues; Login widget tests 4 passed. These are not evidence that the runtime freeze is fixed.
- Safety: this investigation did not write business data, reset credentials, access the main DB/backend, or touch MariaDB/recovery files. Any subsequent preference submission is owner-operated in the test environment.

## Follow-up — 2026-10-02: Waiting verification with Impeller OFF — FAIL

- Continued the existing app PID 5795 without restart, login retest, preference resubmission, or Full E2E. ADB reverse remained tcp:8788 only. Its actual request diagnostics show port 8788.
- Owner-operated preference submission at 07:07:27 UTC received HTTP 200 in 315 ms; ViewModel completed in 334 ms with waiting=true, hasPreference=true and LOADING_FALSE. The immediate status request returned HTTP 200 in 140 ms. These are existing UI-request logs, not a new agent-submitted preference.
- Owner subsequently confirmed Waiting remained frozen/black with Impeller disabled. VM service still responded, main isolate was Resume, and a sampled Dart stack had no active frames. Widget tree still included SmartSearchPage and CircularProgressIndicator, not GroupsPage. WAITING_PAGE_INIT and page heartbeat were absent. Therefore page-specific heartbeat/frame-timing/back-handler instrumentation did not activate; do not claim those checks passed.
- Read-only WidgetsBinding inspection during the freeze: lifecycle=resumed, schedulerPhase=idle, framesEnabled=true, hasScheduledFrame=true, warmUpFrame=false.
- Runtime timeline: last Frame ts=11307869390, Animator::BeginFrame=11307869401, VsyncFireCallback=11307861892 (microseconds). Later pointer events continued: PointerEvent=13626536664, RuntimeController::DispatchPointerDataPacket=13626537380, Engine::DispatchPointerDataPacket=13626537384. The pointer event was about 2,318.7 seconds after the last frame. Android input pending/inbound/wait queues were empty. This proves input reached the engine after frame production stopped; it does NOT prove the intended Waiting button handler received a tap.
- **Actual Waiting UI result: FAIL.** Spinner/countdown/back responsiveness is not verified. No cancel action was performed. Data submitted by the owner was not cancelled, reset, or rewritten by the agent.
- **ON/OFF comparison:** Impeller/Vulkan ON had a frame stall with responsive Dart; OFF initially restored Home/Login but later also stalled after preference submission. Disabling Impeller is not a demonstrated fix and must not become a permanent production setting on this evidence.
- Confirmed failure boundary: frame/Vsync delivery stops while Dart service and engine pointer dispatch remain responsive, before GroupsPage is built. The underlying engine/emulator cause remains unresolved; no speculative Matching/navigation/source patch was made. The next discriminating test is the same build/test backend on a standard Android SDK emulator or physical device, preserving the existing test preference and comparing frame/Vsync behavior. Native-stack collection on this emulator remains restricted without root.
- Files changed this follow-up: this checkpoint only. No analyzer/widget suite rerun because application source was unchanged. No main DB/backend, groups #115/#102, MariaDB datadir, or recovery files were accessed or changed.

## Device comparison checkpoint — 2026-10-02: Android SDK AVD prepared

- Existing Android SDK AVD `Pixel_6` was available. Started it with its existing standard configuration using `emulator.exe -avd Pixel_6 -port 5556`; did not change its config, wipe data, or stop LDPlayer. Boot completed. Target serial is emulator-5556; LDPlayer remains emulator-5554.
- Pixel_6 reports Android 17/API 37, x86_64, Google APIs 16 KB image, configured GPU auto. Installed the exact existing diagnostic APK using install -r (no rebuild): SHA256 `CA4BC462E14B5B5C2756817E47D2BDF03594F5FF87449C99488E46C2BA0A0034`. Launched normally with no Impeller override. PID 7041 reports Impeller OpenGLES and DIAGNOSTICS_READY=true. This differs from LDPlayer's observed Impeller Vulkan backend, so comparisons cannot isolate Impeller alone.
- Device-specific ADB reverse is tcp:8788 only. The APK is the same 8788-configured build already evidenced above; a live request from the new AVD still needs capture to confirm actual use.
- Before testing, read-only matching-status requests to Backend 8788 confirmed DEMO 1139 remains waiting with a preference and no group; 1140/1141 remain none. No preference was submitted, cancelled, or reset by the agent.
- On initial AVD UI inspection, TourApp Home rendered behind an Android **System UI isn't responding** dialog. This is an environment blocker, not proof of a TourApp Waiting failure. Attempted the non-destructive Wait option; asked the owner to handle the dialog if still present and log in manually as DEMO 1139, then open the existing Waiting state without submitting preferences.
- At this checkpoint, no Login request from PID 7041 had been observed. **AVD Waiting: BLOCKED / pending owner login and System UI readiness.** LDPlayer Waiting remains FAIL from prior runtime evidence. No conclusion that the problem is exclusive to LDPlayer yet.
- Changes in this step: checkpoint only; installation of the existing APK on the specifically authorized AVD and device-specific 8788 reverse. No Flutter source/business rules, database rows, MariaDB configuration, or main services changed. Do not use this preparation as a UI PASS or as justification to disable Impeller permanently.

### AVD live UI outcome — 2026-10-02: Waiting responsive

- Owner reported normal operation on the new AVD. PID 7041 logs confirm actual matching requests to 127.0.0.1:8788, HTTP 200, WAITING_PAGE_INIT at 07:53:32 UTC, status waiting=true/hasPreference=true, and overlay=false after loading.
- Important comparison limitation: logs also contain an owner-operated preference submit at 07:53:31 UTC (HTTP 200, 305 ms). The agent did not submit it. Therefore this run is not a strictly controlled reuse of the identical pre-existing preference; do not claim otherwise or revert the owner's data.
- Actual AVD screen observations showed Waiting and successive different spinner positions. Heartbeats between 07:53:42 and 07:55:02 UTC reported 496–574 frames per ten seconds, build samples 1–7 ms, raster samples 11–25 ms, busy=false, overlay=false, lifecycle=resumed. This contrasts with the stalled frame/Vsync timeline on LDPlayer.
- Countdown was visibly 23 hours 59 minutes. Status polling continued with HTTP 200 after one minute. Multiple visible countdown decrements were not captured, so that narrow assertion remains unverified.
- Owner explicitly confirmed the in-page back arrow worked. Runtime logged BACK_HANDLER didPop=true at 07:55:10 UTC, and the Home header was visible afterward. No CANCEL_TAP observed and no cancel request was issued by the agent. Page body pointer counters were zero in the sampled heartbeats; successful back navigation is the direct input/handler evidence, not a fabricated body-pointer count.
- **Waiting on standard AVD: PASS for displayed page, animated spinner, sustained frames, and actual back navigation. LDPlayer: FAIL on prior observed runs.** Findings associate the failure with the previous device/rendering environment; they do not establish the exact LDPlayer/engine defect or prove all Flutter source paths bug-free. Android version and graphics backend differ between devices.
- Recommend Pixel_6 emulator-5556 with the same test APK, normal/default Impeller, and device-specific reverse tcp:8788 for the October 8 demonstration. Select this serial explicitly rather than relying on the first connected emulator. Do not permanently disable Impeller based on these results. Full demo flow and long-duration stability are separate, untested claims in this run.
- No application source changes, credential resets, database cleanup, main DB/backend access, or MariaDB changes were made. This checkpoint is the only project file changed in this follow-up.

### Owner handoff — 2026-10-02

- Owner selected Pixel_6 / emulator-5556 as the primary test/demo device with test Backend 8788 and Portal 4174. Do not resume LDPlayer, Matching V2, or Impeller investigation without a new relevant request.
- Read-only readiness check: GET /api/health on 8788 returned HTTP 200 and databaseConfigured=true; Portal 4174 /agency/login returned HTTP 200. Device emulator-5556 is online, has reverse tcp:8788 only, and TourApp PID 7041 remains running. The initial /health probe used the wrong path; /api/health is the implemented route. Portal page availability is not a new login/E2E claim.
- Services/device were left running without restart. Owner will continue manually using DEMO 1139–1141 and existing Waiting data. No preference submission, cancellation, DEMO cleanup, or main database access was performed. Diagnose only new issues supplied by the owner; no background monitoring was scheduled.

## Saved Account implementation checkpoint — 2026-10-02 (paused at owner's request)

### Target and safety boundary

- Target: Pixel_6 / emulator-5556, test Backend 8788, Portal 4174, test schema tourapp_full_e2e_demo_20261001, DEMO users 1139–1141. Keep existing preferences/waiting. Never use main Backend 8787 or main DB for testing.
- Owner requested a checkpoint before quota reset. Keep all current edits; do not roll them back or start a Full QA/E2E rerun. This is incomplete implementation, NOT a live Saved Account PASS.
- No DB writes, preference submissions/cancellations, credential resets, device reinstalls, Backend restarts, or MariaDB changes were performed during this Saved Account implementation. Current running Backend and installed APK still use the earlier implementation.

### Findings and implemented source changes

- Original Login tiles came from a fixed development-account list, not persistent user-selected accounts; they belonged to a different environment. AppSession was memory-only. Existing member sessions are server-memory tokens; no refresh flow was found. Restarting Backend invalidates these sessions.
- Added SavedAccountStore using flutter_secure_storage. It stores token plus account ID/display metadata, not passwords, under a key namespaced by the complete configured API base URL (trailing slash removed). Reading/removing one namespace does not touch the other backend's namespace. Review same-URL/different-schema isolation if ever reusing a port for another DB; the current implementation isolates by URL, not database identity.
- LoginPage now loads stored accounts rather than developmentAccounts. Normal password login saves the authenticated result. A saved tile calls AuthService.validateSession, checks the returned ID equals the selected ID, and only then replaces AppSession. A 401/403 removes that saved entry and shows the normal credential form without a prefilled password; transient errors leave the saved entry available for retry.
- Added POST /api/auth/session to the existing route allowlist/dispatch. It uses the existing bearer session parser and a read-only query restricted to the session's active user. Returns the existing _userJson shape without password/hash and never issues a new token. This code has NOT been loaded into the running Backend or tested live.
- Added a debug-only Profile button 'สลับบัญชีที่บันทึก' that clears the local session, removes stacked routes, and opens Login while retaining saved server sessions. Existing Logout still calls server logout and revokes its token; the corresponding saved entry is rejected/removed on its next use. Do not silently change Logout to retain a usable token.
- AppSession login/logout now clear local messages and GroupService's in-memory demo/message caches. Home clears previous member state on session changes and retries current-user loading after an old request finishes; existing token checks discard stale responses. Race/cache behavior still requires tests.
- The previous hardcoded development-account source file was NOT modified. It is no longer imported by LoginPage. Do not print or copy its embedded credential values into output or tests.

### Files changed on disk

- pubspec.yaml (flutter_secure_storage constraint ^10.0.0), pubspec.lock (resolved flutter_secure_storage 10.3.4 and platform dependencies).
- lib/services/saved_account_store.dart (new).
- lib/services/auth_service.dart.
- lib/pages/login_page.dart.
- lib/pages/profile_page.dart.
- lib/pages/home_page.dart.
- lib/core/app_session.dart.
- lib/services/group_service.dart.
- server/gemini_proxy.dart.
- Generated by pub get: windows/flutter/generated_plugin_registrant.cc, windows/flutter/generated_plugins.cmake, linux/flutter/generated_plugin_registrant.cc, linux/flutter/generated_plugins.cmake, macos/Flutter/GeneratedPluginRegistrant.swift (and normal Flutter dependency metadata).
- This audit checkpoint.
- IMPORTANT: The last attempted patch, which included rewriting login_page_test.dart, removing an unnecessary import, and adding Android backup configuration, FAILED validation because delete/add targeted the same file. NONE of that patch applied. test/widgets/login_page_test.dart and android/app/src/main/AndroidManifest.xml remain unchanged. No new regression tests have been written successfully yet.

### Verification actually completed

- flutter pub get: PASS.
- dart format on touched files: completed.
- Targeted dart analyze on saved-account store, auth service, login/profile/home pages, AppSession, GroupService and server/gemini_proxy.dart: no errors/warnings; one informational unnecessary_import in profile_page.dart (dart:typed_data is also exported by newly imported foundation.dart). Not yet corrected.
- No updated widget/unit/backend tests executed. Old Login tests still expect fixed development-account tiles and must be updated, not treated as passing for this change.
- No new APK build/install; no secure-storage device validation; no test Backend restart; no live session endpoint validation; no bootstrap of the three saved accounts; no actual saved-tile UI tests.
- Saved Account 1139: NOT TESTED. 1140: NOT TESTED. 1141: NOT TESTED. Cache separation: NOT TESTED. Do not reuse earlier normal-form Login or Waiting PASS as Saved Account proof.

### Continue after quota reset (in order)

1. Review current edits only; do not recreate the work. Remove the unnecessary import. Review secure-storage failure behavior, Android backup policy per plugin guidance, and release gating: tiles/switch button are currently debug-only, while ordinary login currently saves through the store even in release. Decide/fix this consistently within the requested scope. Do not add plaintext fallback.
2. Finish regression tests using mock secure storage and mock HTTP: environment separation, three-account switching, server-validated identity, mismatched ID, expired/suspended session, transient network error, storage failure, normal login saving, logout behavior, message/group/cache clearing, stale in-flight Home requests. Replace old fixed-account expectations without echoing embedded passwords. Add targeted backend authorization/read-only session tests using existing harness where practical.
3. Run targeted analyzer and relevant tests once, fix proven issues, then build the diagnostic APK with API URL http://127.0.0.1:8788 and existing debug diagnostics. Keep standard/default Impeller and use emulator-5556 explicitly. Install -r only; never clear data/uninstall.
4. Identify the existing test Backend launcher/process securely and restart ONLY 8788 to load /api/auth/session. Never stop 8787, MariaDB, or Portal. Preserve the existing test DB configuration. Recheck unauthenticated/invalid session -> 401 and active member identity from the real endpoint. Backend restart invalidates old in-memory sessions; explain this before asking for login.
5. Safest planned bootstrap is one owner-operated normal password login for each DEMO account, saving the returned session to Android secure storage; no bootstrap bypass endpoint has been added. Owner can use tools/show_demo_member_credentials.ps1 and DPAPI credentials already stored at %LOCALAPPDATA%/TourAppE2E/member-credentials.xml. Never print passwords/tokens. If automating bootstrap instead, design and prove debug/local-only scope before implementing; do not inject bearer tokens into source, build defines, plaintext files, logs, or UI screenshots.
6. On Pixel_6, validate actual saved tile sequence 1139 -> Profile 'สลับบัญชีที่บันทึก' -> 1140 -> switch -> 1141, confirming returned API user IDs and absence of prior profile/group/messages. Use switch, not Logout, to retain sessions. Actual UI authentication may require owner interaction under Computer Use restrictions. Mark UI tests BLOCKED until observed, not PASS based on mocks.
7. Read-only checks preserve preferences/waiting; do not submit/cancel. Update this checkpoint with separate automated/API/real-UI evidence and report first-time setup plus expiry/restart limitations.

No passwords, bearer/refresh tokens, or API keys are stored in this checkpoint.

### Saved Account continuation — 2026-10-02: tests/build ready, UI verification in progress

- Tried requested git status/diff before edits: directory has no .git repository. Reviewed the ten checkpoint-listed source/dependency files instead; did not initialize Git or revert prior edits.
- Completed release gating: secure account loading/saving stays debug-only, matching the existing debug-only chooser/switch action. Added Android allowBackup=false to avoid backing up session ciphertext without its device key. Removed the unnecessary profile import. Invalid-session removal now tolerates storage-delete failure while still refusing authentication and returning to password login.
- Replaced obsolete fixed-development-account widget expectations with 13 targeted automated tests. PASS: backend namespace separation/idempotent save, bearer-only session request, 401/403/503 handling, three-account identity/group/message replacement, identity mismatch denial, storage-read failure, normal login saving, and empty namespace without old fixed tiles. First test run failed because a test used the wrong submit-button key; corrected the test to the existing submit-login-button. Second run: 13/13 PASS. No production validation was weakened.
- Targeted dart analyze of the eight changed Dart implementation files plus login_page_test.dart: No issues found. Flutter debug APK build PASS (296.4 seconds), test API base 127.0.0.1:8788 with diagnostics enabled, no renderer override. Installed -r on Pixel_6/emulator-5556 successfully; no clear-data/uninstall.
- At resumption ports 3306/8788/4174 and emulator were not running. Owner started MariaDB via the existing persistent-console method; agent did not restart/repair it. Started existing Pixel_6 AVD explicitly on 5556. Read-only DB identity using the existing test-scoped DPAPI credential returned tourapp_full_e2e_demo_20261001 and tourapp_full_e2e_demo_app@127.0.0.1. Started only test Backend 8788 (PID 8680) with all explicit test DB variables, development-account seeding disabled, no Admin bootstrap password, and Demo slip verifier. Main Backend/database not accessed. Portal not needed for this authentication test and not started.
- Backend /api/health HTTP 200, databaseConfigured=true. Live POST /api/auth/session rejects absent and invalid bearer with HTTP 401. No authenticated live Saved Account assertion yet.
- Existing member sessions are in-memory, so the newly started Backend requires normal first login. No authentication bypass/bootstrap endpoint was added. Opened local owner-operated DPAPI credential viewer and asked owner to login each of 1139–1141 once, then use Profile 'สลับบัญชีที่บันทึก' and tap each saved account. Computer Use prohibits automating authentication dialogs; owner performs authentication gestures and agent checks privacy-safe logs.
- Waiting/Preference data not submitted, cancelled, cleared, or modified by the agent. No schema/main DB/Matching/Payment/MariaDB recovery changes. Current UI outcome is PENDING, not PASS, until owner's actions and runtime identity logs are observed.
- Additional files touched this continuation: test/widgets/login_page_test.dart, android/app/src/main/AndroidManifest.xml, small completion edits in login_page.dart/profile_page.dart, and this checkpoint. Existing implementation changes from preceding checkpoint retained.
