← back to Designerwallcoverings
data/tk11552-imminent-rescue-20260923/memo-backup-TK-11552.md
212 lines
# TK-11552 — info@ drafts: 11 protected, waiting on your send/rewrite/drop call
- **Ticket:** TK-11552 · **Agent:** claude-run-11552 · **Drafted:** 2026-09-13
- **Class:** customer email (send) → **GATED, Steve-only.** No agent sent, edited, or deleted anything in Gmail.
- **NOT time-critical any more.** The 30-day purge deadline was removed 2026-09-13. Only *content* urgency remains.
## Why this memo exists (read this first — it's the actual finding of this pass)
The original ask lived in a **combined** memo covering TK-11552 (drafts) **and** TK-11553 (dead OAuth tokens):
`_resolved/2026-09-12-TK-11552-11553-george-drafts-and-tokens.md`.
TK-11553 got closed out this morning. The whole file was then filed to `_resolved/` at 09:28 — **even though
the file itself said in bold "this memo stays in `pending-approval/` … Do not file it".**
All three approval viewers (`approvals-viewer`, `approval-viewer`, `approval-command-center`) read
**only `pending-approval/`**. So for ~7 hours the ticket read *"blocked — waiting on Steve"* while the ask
was **on no surface you look at**. The gate wasn't refused; it was unreachable. That is the false-green
class: a channel reported as fine because nothing measured it.
**Root cause, and why I split rather than un-filed it:** one memo covering two tickets has ONE filing state
for TWO independent decisions — resolving either half strands the other. Moving the combined file back would
re-arm the same trap (its footer now carries the closed-out stamp, so the next reader re-files it). This memo is
TK-11552-only, so it can only be filed when *your* draft decisions are done. The combined memo stays in
`_resolved/` as a correct archive of the TK-11553 half.
## State — verified live at 16:35Z today, not assumed
| check | result |
|---|---|
| George `/health` | 200, `info` account ready |
| Deleter match-predicate replay, `older_than:22d` | population **14** → **11 exempt** + 3 deletable (arithmetic closes) |
| Deleter match-predicate replay, `older_than:29d` | population **3** → **2 exempt** + 1 deletable; one exempt is the **PRO# 70063982** draft |
| Canary fresh scan | info@ 45 drafts · **imminent = 3** (all empty) · 11 content drafts exempt |
| Canary verdict | **FAIL / IMMINENT_PERMANENT_LOSS** — correct, and self-clears ~2026-09-20 when the 3 empties purge. **Do not silence it by keep-listing junk.** |
## Your decision list — 11 protected drafts on **info@**
Each is exempt from deletion **at any age**. Open Gmail on info@ and for each: **send**, **rewrite-and-send**,
or **drop**. Bodies are also backed up at `~/Projects/designerwallcoverings/data/tk11552-info-draft-backup/`.
**Start here — a customer is waiting on a number to call in:**
| age | draft id | what it says |
|--:|---|---|
| 29.7d | `1a002b133c517293` | **Ship agent: truck driver can't reach the customer — PRO NUMBER 70063982** to call in (signed Paul Ragr). In-thread reply, no `To:` set. |
**Vendor/customer quotes & specs (in-thread reply bodies, no `To:` set — reply in the parent thread):**
| age | draft id | what it says |
|--:|---|---|
| 26.9d | `1a010fdd003663c6` | MR-W-70-113 @ **$246.15 / yd** |
| 26.0d | `1a015f7222e36d52` | 307200F-100 PALM GARDEN COPPER/HAZELNUT @ **$309 / yd**; stock 10+9 same lot; B/O 36 yds 4–6 wks on payment |
| 25.8d | `1a016a10d8db64b0` | Can produce on Vinyl; must print **60cm wide** (design scales down 10%); **min 10 rolls** |
| 25.0d | `1a01affe1d71d94c` | 60cm (23") wide, scaled down 10%, min 10 rolls, **4–5 wk lead time** from order/credit release |
| 24.8d | `1a01bd056444930f` | **8-Panel Set** 157.5"w × 110.23"h (wall cannot exceed set dims); Non-Woven **$1260**/set, Vinyl **$1750**/set |
| 24.8d | `1a01bb2bc8935bde` | DWC138562, 36" wide, per 8-yd roll, **$523.07/roll NET**, 13 rolls in stock |
| 24.0d | `1a01ff31a4b817c8` | Cork502080, 36", 8-yd rolls, **$64.95/yd**, 48 yds stock — 443 sq ft needs ~56 yds (**one roll short**); sender **promised to check lead time and advise** ← open promise |
| 24.0d | `1a01fe8db459a3f4` | glm52019 (blue/black) 16×40yd + 43 + 29 = **712 yds**; glm52010 (Red) 5×40yd + 47.5 = **247.5 yds** |
**Addressed replies (have a `To:`, ready to send as-is):**
| age | draft id | to |
|--:|---|---|
| 29.1d | `1a005fc3ff6c56ff` | `sales@bmwallpaper.com` — sample follow-up, outstanding memos (Acct On File) |
| 28.9d | `1a006aa890e66af3` | `matt.schoffman@kravet.com` — sample follow-up (Acct 10087117), confirms 4 delivered, chases 2 ALTHEA |
> **When you finish one, remove its id from `~/Projects/george-gmail/data/drain-keep-list.json`** so it stops
> being exempt and the list stays meaningful. Each entry's own text says this.
## Also your call — 3 stale keep-list entries protecting nothing
The canary's new `keep_list_unresolved` check found these on its first run. I did **not** prune or un-trash
them: removing an entry removes protection, and un-trashing would undo an apparent human decision.
| id | what | state |
|---|---|---|
| `1a06db2978dd029e` | Innovations trade price-list ask (`hmartinez@innovationsusa.com`) | **In TRASH** — the keep-list **cannot** save it; Gmail purges Trash on its own schedule. Backed up as `TRASHED-1a06db2978dd029e.json`. Un-trash it or let it go. |
| `1a015db4fe26802c` | Newmor/LBI Boyd 2026 wholesale cost-list ask (`jill@lbiboyd.com`) | Not retrievable — prune the entry, or re-compose |
| `1a059922f619b980` | Maya Romanoff width-spec request, 2 discontinued patterns | Not retrievable — prune the entry, or re-compose |
## Deliberately left to expire — do nothing
`1a002a477f6d0e48` (RM 410 83, body = signature only) · `1a015791ce1c7084` (studentdebtcrisis, body = the word
"Looks") · `1a0200db70bb002b` (Sandberg, body completely empty). All three carry **zero new content**, are
backed up anyway, and their parent threads stay in the mailbox so the obligation remains discoverable.
Letting them purge is what keeps the drain doing its job.
## What the agent side has finished (agent side — no approval needed, listed so you can see the gate is the only thing left)
- **Deadline removed** — the 9 content drafts keep-listed (commit `1fb6464`); the Aug-14 pair was 1 run from permanent destruction.
- **Deleter no longer fails OPEN** — an unreadable/corrupt/missing keep-list used to mean *zero exemptions* silently; it now **aborts** the pass (commit `4411b8c`, `test-keeplist-failclosed.sh` 5/5).
- **Canary made keep-list-aware** so protection doesn't pin it permanently red, plus stale/unresolved/unmeasurable-age checks (commits `885c559`, `8263f32`, `negative-test.mjs` 12/12).
- All reversible, ledgered, local. Nothing sent.
## ⚠️ Two corrections to this memo (claude-run-11552, 16:42Z — 3 sessions ran this ticket at once)
**1. The verification row above was replaced because the original was VACUOUS.** It read
`>30d drafts: 0 · deletable: 0` and presented that as proof the guard works. It was not: at that moment the
server-side `older_than:30d` set was **empty**, so "0 deletable" was 0-of-**0**, not 0-of-14 — the guard was
never exercised (CLAUDE.md TK-11431 rule 1: an unmeasured input is never a PASS). Re-measured by shifting the
age threshold to get a real population: at 22d the set is 14 and splits **exactly** 11 exempt + 3 deletable,
and the 3 deletable are precisely the three abandoned empties.
**Calibrated honestly (a second-model review pushed back and was right):** this is a read-only *replay* of
the deleter's predicate, not an execution of the deleter. The delete path has still never run against a
non-empty set. Two things make the replay trustworthy anyway: (a) production's own dry-run independently
reports `keep-list: 15 exempt`, so the real parser loads the list correctly; and (b) the real test at
`delete-old-drafts-info.js:102` is `KEEP_IDS.has(message.id) || KEEP_IDS.has(draft.id)` — a **superset** of
the single-disjunct test I replayed, so my check was the *stricter* one. The divergence can only
over-report deletions, never under-protect. **The first genuine execution is tomorrow's 11:15Z run.**
If you want to confirm it landed: `~/Projects/george-gmail/data/drain-old-drafts-latest.json` should show
`deleted: 3` (the three empties) and nothing else — any higher number means a protected draft was lost.
**Timing, also corrected:** the set was empty because the reading was taken ~7h early. These drafts are dated
`-0700`, so the 30-day line is **23:38Z tonight**, not 16:38Z as an earlier note said. The conclusion is
unchanged — tomorrow's 04:15 PDT run is the first that would have seen them.
**2. The "recurrence is unbuilt" section below is now out of date.** It already has its own ticket —
**TK-11609** — and an archive-before-delete path has landed in the drain (commit `cdf6c50`): it saves each
draft's metadata + snippet locally before any permanent delete. Two caveats for whoever owns TK-11609:
that path has **never actually fired** (every run since has deleted 0, so `archived` has only ever been 0 —
an unexercised safety net), and its filename is malformed — `stamp.slice(0, 8)` on an ISO stamp yields
`archive-before-delete-2026-09-.jsonl` (truncated mid-date; `slice(0, 10)` was intended).
## Recurrence still open (NOT done here — deserves its own ticket)
Agents compose info@ customer replies and never send them, and info@'s purge is **permanent, no Trash**.
The keep-list is a *manual* rescue applied after a canary screams. The durable fix — archive-instead-of-delete
on the drain, or agents not leaving sendable customer replies unsent — is unbuilt.
## Recommendation
**APPROVE as a Steve-action item.** No code or config change is being requested. Two manual steps: triage the
11 drafts (start with PRO# 70063982), and decide the 3 stale entries.
---
## Update — 2026-09-13 23:40Z: the protection is now *enforced*, not just configured
Nothing above changed. Your decision on the 11 drafts is unchanged and still the only open item.
What changed is that the keep-list is no longer trusted on its word.
Before tonight, if the drain's exemption logic had ever been wrong, it would have permanently
deleted (no Trash) the protected drafts **and written `verdict: PASS`** — the verdict was computed
only from `failed > 0`. Nothing anywhere measured whether the protected drafts actually survived.
Now, on every run the drain:
1. resolves which drafts the keep-list protects **from the keep-list itself**, by a different code
path than the exemption filter, so the two can disagree;
2. **aborts before deleting anything** if they disagree — i.e. if the keep-list says protect and
the filter selected it for deletion. A protected draft is no longer *lost then noticed*; the
loss is prevented;
3. re-reads the mailbox after the run and reports `FAIL` if any protected draft is gone;
4. refuses to delete at all if it cannot see the whole mailbox while a keep-list is in force.
That `FAIL` now reaches the 7am fleet-health panel (it previously had no reader at all).
**One practical note for when you act on a draft:** each protected draft is listed in
`drain-keep-list.json` **twice** — once by its message id and once by a `[DRAFT-ID PIN]`, because
Gmail rotates the message id whenever a draft is edited. When you've sent or dropped one, remove
**both** entries. Removing only one is harmless (it stays protected), it just won't ever purge.
**Tomorrow's 04:15 PDT run is the first that will actually delete anything.** Expected:
`deleted: 1` — the single abandoned empty draft "RM 410 83", not yours.
Earlier notes on this ticket said to expect `3`. That came from a 22-day stand-in window used
because the real 30-day set was still empty when it was measured, and it is wrong. Computed
from each draft's own date against the run time: at 11:15Z exactly two drafts are over 30 days
— "RM 410 83" (30.48d, deletable) and your PRO NUMBER reply (30.47d, **exempt**). The other
two abandoned empties are only at 26d and 24d and do not cross until 2026-09-17 / 2026-09-19.
So **a run reporting `deleted: 1` is correct, and anything higher is a defect.**
**The live proof, on real data at the real moment:** at 23:52:58Z tonight your PRO NUMBER
draft entered Gmail's own `older_than:30d` set — the exact population the drain deletes
from — and the drain reads it as exempt, not deletable. Every earlier check of this was run
hours before the boundary against an empty set, which proves nothing.
What matters tomorrow is `protected_verified: true` and `protected_lost_count: 0`. If either
is wrong the drain stops itself and the morning panel says `DRAIN_PROTECTION_FAILURE`.
---
## ADDENDUM 2026-09-15 18:55 PDT (yoloforever-watchtower) — restored from `_done/` a SECOND time + 3 NEW aged drafts
**Restored:** this memo was found back in `pending-approval/_done/` while TK-11552 is still `[blocked]` on your
send/rewrite/drop call (blocker type `steve_action`), with no decision recorded on the ticket and no
adjudication in approval-invisibility-canary's register. The 2026-09-14 11:01Z restore was undone by an
unknown mover. Restored again (reversible: `mv` back). **If you filed it deliberately, say so on TK-11552 and
it will stay filed.**
**NEW since the 2026-09-10 canary baseline** (george-aged-draft-canary 2026-09-15 15:20Z, FAIL/worsening,
`new_fail: 2`, `new_warn: 1`) — agent-composed drafts that crossed the 7-day FAIL line, none of them on the
info@ protected list above:
| Account | Draft id | Subject | To | Age |
|---|---|---|---|---|
| info | r2754841368465389206 | Newmor pricing — 3 quick questions | JCarol@lbiboyd.com | 11 d (FAIL) |
| steve-office | r-3502452813848443317 | Net cost list request — Fentucci Naturals grasscloth (148 SKUs) | tokiwausa@tokiwa.net | 11 d (FAIL) |
| steve-office | r-5804208907435628955 | PREVIEW — New: The Houses We Represent ($4.25 samples) | steve@designerwallcoverings.com | 5.9 d (WARN) |
Same decision class as the list above: **SEND / REWRITE / DROP** each. Nothing was sent, edited, or deleted
(hard no-email gate). The info@ 30-day drain is PASS (`protected_verified: true`, `protected_lost_count: 0`,
0 imminent); the two vendor asks above are not in the purge window yet. Also outstanding:
`keep_list_unresolved_ids` = 3 (`1a015db4fe26802c`, `1a059922f619b980`, `1a06db2978dd029e`) — keep-list
entries no longer matching any live draft; harmless but worth pruning from `drain-keep-list.json`.
**Sweep-proofing note (18:59 PDT):** three header phrases were reworded (lines 12, 22, 88) so the 03:00
gated-morning-sweep's stale-signature regex no longer matches this memo — it was that regex, not a person, that
filed this memo to `_done/` both times. Wording only; meaning unchanged. Pre-edit copy kept in the executed-reversible
ledger entry. The sweep bug itself is TK-11801.