fix(web): GWS verify step — drop fragile JSON-parse + add anti-footgun guidance

Step 8 of the Google Workspace connector prompt told Claude to call
`gws drive files list` + `gws chat spaces list` and parse counts
out of the JSON response. In practice Claude improvised a
`python3 -c 'f"… {len(d.get(\"files\",[]))}…"'` snippet that
fails in two ways: f-string expressions reject backslashes pre-3.12
(SyntaxError), and gws can emit a banner ahead of the JSON body
(JSONDecodeError on `json.load(sys.stdin)`).

Treat exit code 0 from each gws call as success, drop the
`<N> drive file(s), <M> chat space(s) visible` counts (consistent
with the Step 0 precheck), and explicitly warn off the two
anti-patterns. Summary-grep prefix ` Google Workspace ready —` is
preserved so the install-summary still picks the line up.
This commit is contained in:
Vojtech Rysanek 2026-05-21 18:21:33 +04:00
parent 9e3e611aab
commit 487f840596
2 changed files with 11 additions and 1 deletions

View file

@ -87,6 +87,16 @@ CalVer image tags (`stable-YYYY.MM.N`, `dev-YYYY.MM.N`) are produced for every C
standard step-lede size instead of the previous 13px chip. standard step-lede size instead of the previous 13px chip.
### Fixed ### Fixed
- Google Workspace connector prompt's Step 8 verify no longer asks
Claude to parse a row count out of `gws drive files list` / `gws
chat spaces list` JSON. Claude would improvise a `python3 -c 'f"…
{len(d.get(\"files\",[]))}…"'` snippet that fails two ways: f-string
expressions reject backslashes in Python <3.12 (`SyntaxError`), and
`gws` can emit a banner before the JSON body (`json.JSONDecodeError`).
Step 8 now treats exit code 0 as success, drops the `<N> drive
file(s), <M> chat space(s) visible` counts, and explicitly warns
against both anti-patterns. The summary-grep prefix (`✅ Google
Workspace ready —`) is preserved.
- Install-script Step 2 + Step 9 restart cue + post-install `/home` hero - Install-script Step 2 + Step 9 restart cue + post-install `/home` hero
now reference `~/Desktop/<workspace_dir>` to match the `/home` "Step 2 now reference `~/Desktop/<workspace_dir>` to match the `/home` "Step 2
— pick a folder" recommendation users actually run (`mkdir -p — pick a folder" recommendation users actually run (`mkdir -p

View file

@ -343,7 +343,7 @@ _GWS_PROMPT_TAIL_TEMPLATE = """
7. Find where gws stored my credentials (`gws auth status` should show the path; typically ~/.config/gws/ on Unix, %APPDATA%\\gws\\ on Windows). chmod 600 on Unix; on native Windows, restrict ACLs to my user with `icacls "$creds_path" /inheritance:r /grant:r "$env:USERNAME:F"` file is already in my user profile so this needs no admin. 7. Find where gws stored my credentials (`gws auth status` should show the path; typically ~/.config/gws/ on Unix, %APPDATA%\\gws\\ on Windows). chmod 600 on Unix; on native Windows, restrict ACLs to my user with `icacls "$creds_path" /inheritance:r /grant:r "$env:USERNAME:F"` file is already in my user profile so this needs no admin.
8. Verify with two low-impact reads, one per scope group: `gws drive files list --params '{{"pageSize": 1}}'` (Drive scope landed) and `gws chat spaces list --params '{{"pageSize": 1}}'` (Chat scope landed). If both return 200 with valid JSON, print ` Google Workspace ready connected as <my email>. <N> drive file(s), <M> chat space(s) visible.` (exact prefix the final summary grep for it). On any failure, print ` Google Workspace setup failed: <which call failed (drive|chat)>, HTTP/status <code>. <one-line hint to fix (rotate creds | rerun gws auth login --full | etc.)>.` and stop. Never echo tokens, file/message metadata, or scope strings to chat. 8. Verify with two low-impact reads, one per scope group: `gws drive files list --params '{{"pageSize": 1}}'` (Drive scope landed) and `gws chat spaces list --params '{{"pageSize": 1}}'` (Chat scope landed). Treat exit code 0 from each invocation as success do NOT pipe gws output into `python3 -c 'f"..."'` (f-string expressions reject backslashes in Python <3.12, so escaping `\\"files\\"` inside a shell-quoted f-string raises SyntaxError) and do NOT call `json.load(sys.stdin)` on the raw stream (gws may emit log lines or a banner before the JSON body, which trips `JSONDecodeError`). If you really need to count rows for diagnostics, write the stdout to a temp file first and parse it with a plain `json.loads(open(path).read())` inside a `try/except`. If both calls exit 0, print ` Google Workspace ready connected as <my email from `gws auth status`>. Drive + Chat scopes verified.` (exact prefix the final summary grep for it). On any failure, print ` Google Workspace setup failed: <which call failed (drive|chat)>, exit <code>. <one-line hint to fix (rotate creds | rerun gws auth login --full | etc.)>.` and stop. Never echo tokens, file/message metadata, or scope strings to chat.
9. Remind me how to revoke later: `gws auth logout` clears local creds; the OAuth grant also appears at https://myaccount.google.com/permissions for Google-side revocation.""" 9. Remind me how to revoke later: `gws auth logout` clears local creds; the OAuth grant also appears at https://myaccount.google.com/permissions for Google-side revocation."""