# Agency lead detail #115 — focused fix

## Confirmed root cause

`MySqlAgencyAdminRepository.findEligibleLead` returned `_withLeadSummary(...)` without awaiting it inside try/finally. The finally block tried to close the connection while the first aggregate query was in flight.

Read-only reproduction against current group #115 with the existing backend DB configuration produced:

`MySQLClientException: Can not close connection. Connection state is not in connectionEstablished state`

Stack: `MySQLConnection.close` -> `MySqlAgencyAdminRepository.findEligibleLead` (line 444 before the fix).

The production handler maps this uncaught exception to HTTP 500 and `ระบบเกิดข้อผิดพลาด กรุณาลองใหม่`. This mapping was inspected in source; the existing browser's Network response and backend console were not captured.

## Fix and validation

- Production change: add `await` to the existing `_withLeadSummary` return. No SQL, schema, authorization or response contract changes.
- New async connection regression failed for both poll/no-poll cases before the fix and passed after it. The final test exercises `/api/agency/leads/detail` through AgencyAdminApi, with an approved in-memory fixture session, real repository control flow, and an async DB test double.
- Assert API response 200, aggregate maps, anonymized member preferences, no per-member poll choices, and connection closure after all seven queries complete.
- Related tests: 15 passed (2 connection regressions, privacy, Agency/Admin API). Final enhanced API regressions rerun: 2 passed.
- Targeted Dart analysis: no issues. Portal TypeScript typecheck: passed.
- Read-only live repository after fix: group #115 present; region aggregate Beijing 2, Shanghai 0, Xi’an 0; place aggregate empty; 3 anonymized member preferences; individual poll choice fields absent. The current city round need not be finalized to load this detail.
- Backend JSON maps match Portal Lead/MemberPreferenceDetail types and rendering. No frontend rewrite needed.

## Live UI limitation / next checkpoint

Backend 8787 and Portal 4173 were already running. Edge had a Travelin Portal window, but Computer Use stopped because it could not confidently determine the browser URL. No further browser input was attempted. Existing Agency session, Console/Network, and the rendered fixed page remain unverified.

The existing backend was NOT restarted, so it still needs a normal backend restart to load this Dart source change. Restarting will invalidate its in-memory sessions; a valid Agency login is needed afterward. No account was reset and no credentials/tokens were stored in this report.

## Data preservation

All live diagnostic connections used `SET SESSION TRANSACTION READ ONLY`. No members, votes, poll status, group status or business data were modified. No Matching/Payment/schema changes. MariaDB was not restarted or repaired. The temporary read-only probe was removed and its test process stopped; existing Backend/Portal remain running.

## Follow-up verification — 2026-09-27

- Inspected listeners before restart: Backend PID 4976 (`dart.exe run server\\gemini_proxy.dart`), MariaDB PID 23208, Portal PID 18976. Stopped only the verified Backend process; 8787 became free before relaunch. MariaDB and Portal PIDs remained unchanged.
- Started the current Backend source in a persistent console session (PID 10188) with development-account seeding disabled for this restart; database config and Gemini key were loaded from the existing server configuration. No bootstrap-admin credentials were supplied. Health: `ok=true`, `databaseConfigured=true`, `aiConfigured=true`.
- Anonymous `POST /api/agency/leads/detail` returned HTTP 401, as expected. No authorized Agency session was available in the accessible browser profile; no credential was guessed, reset, or entered.
- Opened `/agency/leads/115` in the available browser. It redirected to `/agency/login`; therefore the actual authorized detail view and its live Network/Console response remain unverified. Browser Console showed no captured errors on the login page.
- Re-ran `flutter test test/server/agency_lead_detail_connection_test.dart`: 2/2 passed, covering the real lead-detail API handler/repository path with poll present and absent, aggregate-only poll output, no member IDs or individual poll choices, and query completion before connection close.
- The prior checkpoint's read-only live repository result for group 115 remains the available authorized-data evidence: region aggregates Beijing 2 / Shanghai 0 / Xi’an 0; place aggregate empty while no place result exists; no per-member vote choices.
- No source files or business/database rows were changed during this follow-up. Group 115 membership, votes and group status were not modified. No MariaDB restart occurred.

### Remaining confirmation

To claim the live `/agency/leads/115` UI is fixed, open the page in a browser with a valid approved Agency session and verify the detail and aggregate poll summary. This could not be completed in the available browser session; the code fix and API regression are verified, but the authorized live UI is not.
