{"slug":"draft-viewer","total":7,"limit":100,"offset":0,"since":null,"commits":[{"hash":"955b609","date":"2026-09-10 09:22:46 -0700","author":"Steve Abrams","subject":"TK-11231: archive + clear the 7 superseded vendor drafts","body":"Steve 2026-09-10 'approve all 4, ungate and run'. Same discipline as the 13:\nfull bodies archived before deletion, since Gmail drafts.delete has no Trash.\n7/7 deleted, 0 failures; the 5 READY vendor asks verified untouched.\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>"},{"hash":"584f7f4","date":"2026-09-10 09:03:28 -0700","author":"Steve Abrams","subject":"Prove the snapshot actually restores — restore.mjs, rehearsed end to end","body":"A snapshot nobody has replayed is a claim, not a backup. Same lesson as\ndw-restore-rehearsal, which exists because a pg_dump ran green for 12 days while\nproducing output that would not restore. Codex flagged the same gap here.\n\nrestore.mjs replays a snapshotted body back into Gmail as a NEW draft. It can\nonly create drafts, so it structurally cannot deliver mail. Dry-run by default.\n\nRehearsed three times against live Gmail, recipient redirected to an internal\naddress so no real vendor draft was duplicated. Result: the recipient-visible\nmessage round-trips byte-identical (516 == 516 chars). The only delta is\nGeorge's provenance banner carrying the restore timestamp, which is correct.\n\nThe rehearsal caught a real defect: restore was NOT idempotent. George prepends\na 'From job:' banner to every draft it creates, so each restore stacked another\none. The first strip attempt failed because the banner is a styled <div>\nwrapping <strong>From job:</strong> — matching the bare phrase left the wrapper\nbehind. Now strips the whole leading div block; verified 1 banner in, 1 out.\n\nAll three rehearsal drafts deleted; info@ is back to its exact 41-draft baseline.\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>"},{"hash":"3fb2245","date":"2026-09-10 09:01:04 -0700","author":"Steve Abrams","subject":"TK-11231: archive + delete the 13 stale customer replies per Steve's call","body":"Steve answered the gated question directly on 2026-09-10: delete all 13,\noverruling the DTD panel's send-all recommendation. His original 09-04\ninstruction stands.\n\nArchived every draft's full body, recipient, subject and date BEFORE deleting\n(data/archive/), because Gmail drafts.delete is permanent with no Trash. The\nGmail artifacts are gone; the content is not. Snapshot-first is house practice\nfor any irreversible delete.\n\nScope was held exactly to the 13 he named: the 5 READY vendor asks, the 7\nsuperseded vendor drafts, and the 2 info@ drafts he chose to let expire were\nall left untouched. steve-office 136 -> 123, info 41 unchanged.\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>"},{"hash":"f7ed61f","date":"2026-09-10 08:59:09 -0700","author":"Steve Abrams","subject":"Rebuild the board around Steve's 2026-09-10 decisions — send 7, keep 13, 7 inert","body":"The old board was split ready/delete, which no longer matches reality: Steve\nruled send-both-fuse-drafts + send-all-5-vendor-asks, 'put in drafts' for the 13\ncustomer replies, and that HE clicks Send in Gmail. So the board is now grouped\nby action and it neither sends nor deletes anything.\n\n- build-board.mjs derives every age from Gmail's own internalDate in the\n  snapshot rather than a hand-typed number, so the fuse cannot silently drift.\n  It reads 3d (bmwallpaper) and 4d (Kravet) until permanent deletion.\n- The two fuse drafts were never on the old board at all; they are now the\n  first thing on the page.\n- Cards carry the created date+time chip and a snapshot marker, so it is\n  visible at a glance that the text survives even if Gmail loses the draft.\n\nVerified: 401 gate healthy, 200 authed, 7/13/7 cards, 27 date chips, rendered\nin a real browser.\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>"},{"hash":"b443b7c","date":"2026-09-10 08:55:49 -0700","author":"Steve Abrams","subject":"Snapshot every tracked draft body to disk — deletion is no longer irreversible","body":"com.steve.george-drain-old-drafts calls Gmail drafts.delete, which has no Trash,\nso every decision on TK-11231 was previously one-way. 27/27 live drafts are now\ncaptured on disk, including both info@ drafts on the ~4-day permanent-delete fuse.\n\nCaught two bugs in the snapshot itself before trusting it:\n- The two George routes name the body field differently (/api/messages/:id ->\n  body, /api/info/messages/:id -> bodyHtml/bodyText). Reading only `body`\n  returned '' for all 5 info@ drafts with a 200 OK.\n- 'Wrote a file' was being counted as 'saved'. An empty body now counts as a\n  failure, so the report cannot read green while capturing nothing.\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>"},{"hash":"20c1dc8","date":"2026-09-10 08:06:23 -0700","author":"Steve Abrams","subject":"draft-viewer: make the board honest — verify triage against live Gmail — TK-11231","body":"The board was serving a frozen 2026-09-04 snapshot and had started to LIE: one\nlisted draft (info@ 'Newmor 2026 Cost List') had already been permanently\ndeleted by com.steve.george-drain-old-drafts, but rendered as still actionable.\n\nrefresh.mjs was broken two ways: it called /api/gmail/list (no such route — the\nreal ones are /api/search and /api/drafts) and grepped GEORGE_BASIC_AUTH_PASS\nout of secrets-manager/.env, where that key does not exist (verified) — hence\nthe 401 the prior session hit and could not explain.\n\nIt also had a design flaw worth more than the bugs: it rebuilt drafts.json from\na flat API list, which would have OVERWRITTEN the READY-vs-DELETE triage with\nraw data. That classification is judgement and is not derivable from Gmail — it\nis the only thing the viewer is for. Now it PRESERVES the triage and refreshes\nLIVENESS instead, and on a fetch failure it exits without touching the file\nrather than marking everything gone (a false all-clear beats no board — never\nshow a false empty).\n\nViewer renders x.live===false as a struck-through, dimmed card with a GONE\nbadge, plus a 'verified <time>' stamp — and says NOT VERIFIED in red when the\ndata has never been checked, so staleness is visible rather than implied.\n\nVerified live: 5 ready, 21 delete, 1 GONE, auth gate returns 401 unauthenticated.\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VTxE4MgnygQ9EY2rPZvtcK"},{"hash":"e93ef88","date":"2026-09-04 10:40:58 -0700","author":"Steve Abrams","subject":"Draft triage viewer — 5 ready to send, 21 to delete, deep-linked to Gmail","body":"Basic-auth board over the unsent agent-composed drafts found in TK-11231.\nGreen = ready to send (with the catalog gap each one closes), red = delete,\namber = the 13 stale customer replies. Density slider + created-time chip per\nthe standing admin-card rules. Snapshot in data/drafts.json; refresh.mjs will\nrepopulate once George's list endpoint auth is sorted (currently 401).\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01KwSKeavk6pXdx4CymWBXmz\n"}]}