From 9d000e9b7a22668ce035c938c7bf2e3786241e3e Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 26 Sep 2026 12:37:35 +0000 Subject: [PATCH 1/2] feat(website): a visitor's channel rides on into the bot link A visitor from a tagged link who presses "open in Telegram" reached the bot as nobody from nowhere. The visit's channel is remembered at arrival and put at the end of our bot's /start payload at the click (t27ai_bot only, never past Telegram's 64 characters, never over a channel already there), the way the bot reads it (999-multibots-telegraf splitStartSource). Contract: 16 checks; mutations (no 64 limit, any bot) are caught. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FJktaYxNrfAUdxSwdQw3gV --- apps/website/qa/traffic-arrival-contract.mjs | 16 ++++- apps/website/src/lib/trafficArrival.ts | 68 ++++++++++++++++++++ apps/website/src/main.tsx | 7 +- 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/apps/website/qa/traffic-arrival-contract.mjs b/apps/website/qa/traffic-arrival-contract.mjs index 1c008a7c0f..016fc3ea97 100644 --- a/apps/website/qa/traffic-arrival-contract.mjs +++ b/apps/website/qa/traffic-arrival-contract.mjs @@ -2,7 +2,7 @@ // once, untagged visits never, and a failure never reaches the page. // Run: node --experimental-strip-types qa/traffic-arrival-contract.mjs import assert from 'node:assert/strict' -import { arrivalFromSearch, reportArrival, ARRIVAL_URL } from '../src/lib/trafficArrival.ts' +import { arrivalFromSearch, reportArrival, ARRIVAL_URL, carrySource, SOURCE_KEY } from '../src/lib/trafficArrival.ts' // The form the agents publish: the query BEFORE the hash route. const blog = new URL('https://t27.ai/?utm_source=x&utm_medium=social&utm_campaign=blog&utm_content=gf-t#/blog/gf-t') @@ -26,4 +26,16 @@ assert.deepEqual(calls, [[ARRIVAL_URL, { source: 'reddit' }]]) const failing = async () => { throw new Error('offline') } assert.equal(await reportArrival('?utm_source=threads', failing, { getItem: () => null, setItem: () => {} }), false) -console.log('traffic-arrival contract: 9 checks passed') +// The channel rides on into the bot: remembered at the arrival... +assert.equal(store.get(SOURCE_KEY), 'reddit', 'the visit remembers its channel') +// ...and put at the end of OUR bot's /start payload, the way the bot reads it. +assert.equal(carrySource('https://t.me/t27ai_bot?start=website', 'x'), 'https://t.me/t27ai_bot?start=website__x') +assert.equal(carrySource('https://t.me/t27ai_bot?start=foundry', 'Reddit'), 'https://t.me/t27ai_bot?start=foundry__reddit') +assert.equal(carrySource('https://t.me/other_bot?start=website', 'x'), 'https://t.me/other_bot?start=website', 'never somebody else\'s bot') +assert.equal(carrySource('https://t.me/t27_lang', 'x'), 'https://t.me/t27_lang', 'a channel link is not a bot start') +assert.equal(carrySource('https://t.me/t27ai_bot?start=website__x', 'reddit'), 'https://t.me/t27ai_bot?start=website__x', 'a channel already there stays') +assert.equal(carrySource('https://t.me/t27ai_bot?start=website', null), 'https://t.me/t27ai_bot?start=website', 'no channel, no tag') +const long = 'a'.repeat(62) // + '__x' = 65 > 64 +assert.equal(carrySource(`https://t.me/t27ai_bot?start=${long}`, 'x'), `https://t.me/t27ai_bot?start=${long}`, 'never past 64 characters') + +console.log('traffic-arrival contract: 16 checks passed') diff --git a/apps/website/src/lib/trafficArrival.ts b/apps/website/src/lib/trafficArrival.ts index 8f62bb9a6d..2374918757 100644 --- a/apps/website/src/lib/trafficArrival.ts +++ b/apps/website/src/lib/trafficArrival.ts @@ -51,6 +51,12 @@ export async function reportArrival( ): Promise { const tags = arrivalFromSearch(search) if (!tags) return false + try { + // The channel of this visit, for the links into our bot (carrySource). + if (!storage?.getItem(SOURCE_KEY)) storage?.setItem(SOURCE_KEY, tags.source) + } catch { + /* no storage: the bot link goes untagged */ + } try { if (storage?.getItem(ONCE_KEY)) return false storage?.setItem(ONCE_KEY, '1') @@ -69,3 +75,65 @@ export async function reportArrival( return false } } + +/* + * THE CHANNEL RIDES ON INTO THE BOT (owner, 2026-09-26: "end-to-end + * conversion from t27.ai"). + * + * A visitor from X who presses "open in Telegram" used to reach the bot as + * nobody from nowhere: the visit was counted, the start was not tied to it. + * The bot reads the channel at the end of its /start payload after `__` + * (999-multibots-telegraf src/navigation/helpers/appLinks.ts + * splitStartSource) and counts the start by channel; this puts it there, at + * the moment of the click, for OUR bot's links only, never past Telegram's + * 64 characters, never over a channel already in the link. + */ +export const SOURCE_KEY = 'traffic:source' +export const OUR_BOTS = ['t27ai_bot'] +const START_MAX = 64 + +/** The bot link carrying this visit's channel; anything else as given. Pure. */ +export function carrySource(href: string, source: string | null): string { + const tag = String(source ?? '') + .toLowerCase() + .replace(/[^a-z0-9-]+/g, '-') + .replace(/^-+|-+$/g, '') + .slice(0, 32) + if (!tag) return href + let u: URL + try { + u = new URL(href) + } catch { + return href + } + if (!['t.me', 'telegram.me'].includes(u.hostname.toLowerCase())) return href + if (!OUR_BOTS.includes(u.pathname.replace(/^\//, '').toLowerCase())) return href + const start = u.searchParams.get('start') + if (!start || start.includes('__')) return href + const next = `${start}__${tag}` + if (next.length > START_MAX || !/^[A-Za-z0-9_-]+$/.test(next)) return href + u.searchParams.set('start', next) + return u.toString() +} + +/** Tag our bot links as they are pressed. Never throws, never blocks a click. */ +export function installBotLinkCarry( + doc: Pick | null = typeof document !== 'undefined' ? document : null, + storage: Pick | null = typeof sessionStorage !== 'undefined' + ? sessionStorage + : null +): void { + if (!doc) return + const tag = (e: Event) => { + try { + const a = (e.target as Element | null)?.closest?.('a[href]') as HTMLAnchorElement | null + if (!a) return + const next = carrySource(a.href, storage?.getItem(SOURCE_KEY) ?? null) + if (next !== a.href) a.href = next + } catch { + /* a link stays as it was */ + } + } + doc.addEventListener('click', tag, true) + doc.addEventListener('auxclick', tag, true) +} diff --git a/apps/website/src/main.tsx b/apps/website/src/main.tsx index 7b65e21917..a374dc4eb7 100644 --- a/apps/website/src/main.tsx +++ b/apps/website/src/main.tsx @@ -12,7 +12,7 @@ import GlobalStarfield from './components/GlobalStarfield.tsx' import { handExplorerLinksToQueen } from './lib/queenFrame' import { redirectLegacyQueen } from './lib/legacyQueenRedirect' import { triIdentity } from './lib/triIdentity' -import { reportArrival } from './lib/trafficArrival' +import { installBotLinkCarry, reportArrival } from './lib/trafficArrival' // THE QUEEN MOVED to https://app.t27.ai/queen/, which now builds this same // bundle and serves the board on the app's own origin. #/queen here is the old @@ -31,7 +31,10 @@ const leaving = redirectLegacyQueen() // A visitor from a tagged link (utm_source=x, reddit, threads...) is counted // once, by channel, where the agents' links land. Not for a page that is on // its way to another address. See src/lib/trafficArrival.ts. -if (!leaving) void reportArrival() +if (!leaving) { + void reportArrival() + installBotLinkCarry() +} // In a Queen tab's frame, a link to another Explorer switches the Queen's tab instead // of navigating the frame under a rail that names a different one. From d29d4e06ebb019209df2771dc3dfaef22501779a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 26 Sep 2026 12:49:19 +0000 Subject: [PATCH 2/2] fix(website): QueenLanes fallback matches the declared --hud-panel queen-fallback-parity is red on main: the lanes fallback said 0.86 while --hud-panel is declared 0.88. Ported here so this PR can go green; it no-ops once main carries the same line. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FJktaYxNrfAUdxSwdQw3gV --- apps/website/src/components/QueenLanes.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/website/src/components/QueenLanes.css b/apps/website/src/components/QueenLanes.css index a406688d61..424922c7a9 100644 --- a/apps/website/src/components/QueenLanes.css +++ b/apps/website/src/components/QueenLanes.css @@ -9,7 +9,7 @@ min-height: 0; height: 100%; border: 1px solid var(--hud-line, rgba(0, 255, 136, 0.22)); - background: var(--hud-panel, rgba(2, 8, 6, 0.86)); + background: var(--hud-panel, rgba(2, 8, 6, 0.88)); clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px); }