channel-availability.tsfeat(inbox,two-factor,marketing): channel filter on query tabs, 2FA policy, host routing
Three in-flight threads plus supporting docs, committed together.
- inbox: the channel badge no longer goes empty on a Gmail-query tab —
participatingChannels stops dropping chat channels when the tab rule is
mailOnly, so an explicit single-channel badge asks the sources it names.
- two-factor: policy module with its own tests, wired through session
verification and auth, with the env and runtime-contract keys it needs.
- marketing: host routing for the signed-out root, with a redirect test.
- docs: inbox channel-filter bug writeup, daily agenda templates,
open-tracking v2 notes, and the 2026-08-30 customer feedback audit.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 1, 2026, 11:53 AMhotkey-targets.tsfix(unibox): a chat id the feed forgot still isn't a Gmail thread
`splitHotkeyTargets` decided what a selection id was by asking the loaded feed, and read a
lookup MISS as "email". But selection and hover state outlive a feed replacement, so a miss
is an ordinary event — and on one, a `li:`/`wa:`/`slack:` id went to the Gmail driver, which
is precisely the modify-a-thread-that-does-not-exist this module exists to prevent.
The prefix is structural (`services/inbox/feed-core.ts` mints it) and an email row's id is a
bare Gmail thread id, which is hex and cannot hold a colon — so the prefix answers the
question without the feed. An id that is a chat but resolves to nothing is now dropped from
both halves: there is no row to act on, and no driver that could act on it.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 7, 2026, 4:40 PM