diff --git a/README.md b/README.md index a0524e7..53546a0 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ for the Builder's External-only filter, and reading the connected admin and its | Sign out everywhere | Users, Builder | not yet | | Revoke access: app passwords, backup codes, OAuth tokens, and sign out (`deprovision signout`) | Offboarding | **confirmed** (the first live offboarding, 2026-10-07, GAM 7.48.14) | | Suspend / unsuspend | Users (both), Builder (suspend) | not yet | -| Delete an account | Users, Builder | not yet | +| Delete an account | Users, Builder | **confirmed** (2026-10-08, GAM 7.48.22, from the user page: the offboarded leaver, after the transfer completed) | | Undelete an account | Builder | not yet | | Transfer Drive + calendar data | Offboarding, Builder | **confirmed**: as two calls, and the single call offboarding makes (`drive,calendar … all`) ran (the first live offboarding, 2026-10-07, GAM 7.48.14), and the operator checked the effects in Google afterwards: the transfer completed | | Remove the leaver from everyone's calendars | Offboarding | **confirmed** the shape (the first live offboarding, 2026-10-07, GAM 7.48.14): GAM visited every user and ended, as expected, on the leaver's own calendar ("Cannot change your own access level", exit 50, tolerated); the operator checked the effects in Google afterwards | diff --git a/docs/domains/lifecycle-offboarding.md b/docs/domains/lifecycle-offboarding.md index 8eebf63..d67636f 100644 --- a/docs/domains/lifecycle-offboarding.md +++ b/docs/domains/lifecycle-offboarding.md @@ -326,8 +326,7 @@ on **every** offboarding and is tolerated as `OWN_ACL` (the mock's default sweep `SWEEPCLEAN` keeps the exit-0 path for a leaver suspended first). The operator then worked through the after-run checks in Google and reported every effect as expected: the transfer completed, the manager has the mailbox, an outside sender gets the auto-reply, the sweep and the reminder did their -part. Still unproven live: the reminder with an invitee (`sendupdates all`; this run had none) and -`delete_user`. +part. The account delete followed live on 2026-10-08 (GAM 7.48.22, from the user page, after the transfer completed): `delete user` ok. Still unproven live: the reminder with an invitee (`sendupdates all`; this run had none). ## First live run checklist Offboarding a real user is the live test (plan D8; first run 2026-10-07, all eight `ok` — see diff --git a/docs/failure-log/2026-10-08-a-csv-download-opened-as-a-page-in-the-app-window.md b/docs/failure-log/2026-10-08-a-csv-download-opened-as-a-page-in-the-app-window.md new file mode 100644 index 0000000..d075fec --- /dev/null +++ b/docs/failure-log/2026-10-08-a-csv-download-opened-as-a-page-in-the-app-window.md @@ -0,0 +1,19 @@ +# 2026-10-08 — A CSV download opened as a page in the app window, with no way back + +- **Symptom:** in the app, Builder → a read → **Download CSV** replaced the window's page with the CSV + shown as text. The window has no Back control (and pywebview disables the Delete-key back), so the + operator could only quit and relaunch. The Audit export and the onboarding template CSV did the same. +- **Cause:** pywebview's macOS backend downloads a response only when WebKit can't display it + (`decidePolicyForNavigationResponse` → `canShowMIMEType`) or when the link carries the HTML + `download` attribute (`decidePolicyForNavigationAction` → `shouldPerformDownload`). A CSV is + text, so WebKit displays it, ignoring `Content-Disposition: attachment`; none of the three links had + `download`. `ALLOW_DOWNLOADS = True` in `app.py` only enables those two paths. +- **Why not caught:** every test and the accessibility run use TestClient or headless Chrome, where an + attachment response downloads whatever the link says; CI builds the `.app` but never launches it, so + WKWebView's choice was never exercised. +- **Fix:** `download` on every link to an attachment route (`audit.html`, `onboarding.html`, + `_records_meta.html`); WebKit then hands it to pywebview's save panel, in the window's own session (the + token cookie included). +- **Prevention:** `tests/test_app_window_downloads.py` finds every GET route that sends + `Content-Disposition: attachment` and fails unless each is linked, and every link carries `download`. + Confirmed in the rebuilt app the same day: Download CSV opens the save panel and the page stays put. diff --git a/docs/plans/2026-09-23-ten-out-of-ten.md b/docs/plans/2026-09-23-ten-out-of-ten.md index f4b6705..2ef6937 100644 --- a/docs/plans/2026-09-23-ten-out-of-ten.md +++ b/docs/plans/2026-09-23-ten-out-of-ten.md @@ -222,7 +222,7 @@ plan's Phase 0 F1–F3. scope, and D6 says none for now. - **Phase 8** — **done** for offboarding (D8). The operator's first real offboarding ran 2026-10-07, all eight steps `ok`, and its effects checked in Google; README "Live verification status" and the - offboarding runbook record it. Still open: the reminder with an invitee, and the account delete. + offboarding runbook record it; the account delete ran live 2026-10-08. Still open: the reminder with an invitee. **Beyond the plan**, also on the branch (each has a failure-log entry): every confirm step runs what its preview held, under a single-use token — offboarding, signatures, onboarding, bulk department, diff --git a/gamgui/web/templates/_records_meta.html b/gamgui/web/templates/_records_meta.html index 2c0b33d..2607442 100644 --- a/gamgui/web/templates/_records_meta.html +++ b/gamgui/web/templates/_records_meta.html @@ -1,4 +1,4 @@ {# How many rows match of how many, and the CSV of exactly those — in the result's tools row, and swapped there out of band each time the filter answers. #} -{% if filtered %}Download {{ "these " ~ "{:,}".format(matched) if matched > 1 else "this row" }} as CSV{% else %}Download CSV{% endif %} ↓ +{% if filtered %}Download {{ "these " ~ "{:,}".format(matched) if matched > 1 else "this row" }} as CSV{% else %}Download CSV{% endif %} ↓ diff --git a/gamgui/web/templates/audit.html b/gamgui/web/templates/audit.html index 33d6bde..8da84f2 100644 --- a/gamgui/web/templates/audit.html +++ b/gamgui/web/templates/audit.html @@ -9,7 +9,7 @@
{{ total }} action{{ "s" if total != 1 else "" }} logged · {{ failures }} failure{{ "s" if failures != 1 else "" }}
- Export CSV ↓ + Export CSV ↓ diff --git a/gamgui/web/templates/onboarding.html b/gamgui/web/templates/onboarding.html index 305fa61..68bbda1 100644 --- a/gamgui/web/templates/onboarding.html +++ b/gamgui/web/templates/onboarding.html @@ -107,7 +107,7 @@