# Dashboard navigation after AAI login Qualified on 2 November 2026 for [issue #990](https://github.com/pharos-ai-factory/pharos-platform/issues/990). The original qualified build was based on acceptance PR #987 at `16558ce4fe60fc827dfbc7eff3647593d1019d2e`. The implementation and regression below were subsequently carried unchanged onto `main` at `8749b823941868a3d3d1f8c088b697635b7ba324`. The recorded deployed results remain evidence for the original acceptance build; checks of the main-based branch are recorded separately in its pull request. ## Cause and regression Two diagnostic runs against the unchanged `dev-vasilis-m3` deployment reproduced the failure on their fourth and eighth independent sessions: ten passed and two failed. The failing pages remained on `/onboarding` with `document.readyState === 'complete'`, after `/auth/v1/session` reported current policy and ready GitLab access. The second run captured WebSocket close code `4501` on reconnection, followed by no further connection attempt. The browser's load event had already completed. The API times a one-second connect grant before querying session and audience authority. Those queries can consume the grant, including when a shorter read crosses a Unix-second boundary. The handler returned terminal `4501` for this expiry. The [Centrifugo protocol](https://centrifugal.dev/docs/server/proxy#return-custom-disconnect) instructs clients to stop reconnecting for that code. The fix returns retryable `4001` for elapsed authority. Each retry reads session and audience authority again. It preserves the original grant deadline, logout fence, owner expiry caps and terminal denial of invalid sessions or unresolved audiences. Browser polling, readiness delivery and acceptance waits are unchanged. `TestRealtimeConnectRetriesElapsedAuthority` uses Go's virtual clock to cover slow session reads, slow audience reads, a second-boundary crossing and an expired owner deadline. All four cases failed with `4501` before the fix. They now check that expiry grants no subscriptions, a fresh retry restores bounded subscriptions, and a revoked session still receives terminal denial. ## Tested source and live results The deployed UI and all other services remained at `16558ce4fe60fc827dfbc7eff3647593d1019d2e`. A separate checkout of that revision with only the API fix supplied a temporary production API image: - OCI image index: `sha256:77a6537b1e96cdd94d0c668759d041ffc7cbe934dedcce86e17f0dbf9323807f`. - API `server.go` SHA-256: `6572c1e6101ace879cb8e78349c9ec910fea5737ec607f7172d240aad5d33cef`. - The temporary rollout used the repository's fenced `pharos` takeover lease and an image-only, resource-version-checked Deployment patch. | Check | Result | | --- | --- | | `pnpm verify` on the #987-based branch | Passed, including Go race tests, Python suites, web builds, architecture checks and infrastructure renders. | | `pnpm check:large-file-review` | Passed. | | API HTTP tests, `go test -race -count=1 ./adaptors/http` | Passed, including the four expiry regressions. | | `scripts/api_native_sessions.py` on `builder-m3` | Passed against disposable Valkey with race detection; covers session revocation, readiness publication and limited-session authority. | | `pnpm acceptance:run --grep @authentication --repeat-each 10` | 20 passed in 102.185 seconds; one worker, zero retries, zero skips. | | Additional diagnostic sessions | 12 passed; each reached the dashboard automatically and verified logout with HTTP 401. | | Provider verification after logout | Correct fixture identity, six namespace memberships, current AUP and zero active impersonation tokens. | Both acceptance scenarios used fresh browser contexts. Setup observed readiness and automatic navigation without reloading or visiting the dashboard directly. The sign-out scenario reached its UI action and session-termination assertion on all ten repetitions. Private evidence is retained in `.local/acceptance/issue-990/`, including `diagnosis.json`, `diagnosis-close-codes.json`, `diagnosis-fixed.json`, `results/results.json`, `report/index.html` and the test logs. Diagnostics contain allowlisted route categories, timestamps, response statuses, readiness booleans and close codes. They contain no frame payloads, cookies, passwords or page text. Screenshots, traces, videos and full DOM snapshots remained disabled. The reports and their embedded ZIP entries were checked for the fixture password and GitLab token prefixes; neither was present. The original API image was restored and both `pharos` and `themelio-compat` returned to Synced/Healthy at the recorded deployment revision. The lease was released and the temporary image removed. Native takeover was unavailable because the workstation's root daemon was stopped; Docker takeover on the build host failed on traffic-agent injection. These attempts establish no takeover coverage. The disposable GitLab and Keycloak identities, fixture groups, administration resources and local credentials were removed; the original browser tunnel was restored. Standalone PostgreSQL/RabbitMQ suites, the full disposable infrastructure and assembled-authentication suites, manual UAT, Minikube, a fresh k3s installation and a full deployment were not run. Live browser/provider checks and the scoped API rollout on the existing k3s dev box provide the deployment evidence above.