Skip to content
193 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)1/**
2 * The landing page's product tour: one story told as a timeline. Every
3 * frame is a pure function of the time into the loop, so the tour can be
4 * paused, resumed, jumped to a step, or shown still, and the server and
5 * the first client render agree (t = 0).
6 *
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today7 * The story opens on the code: Otto's pull request #431 runs its checks,
8 * is approved and is merged by the queue. Then where it came from: the
The Support Specialist agent is Sam now, not Izzy, in its template, the home page tour, the marketing page and the docs, and the tour's Chat sidebar matches the app's9 * ask in #web, handed to @otto by g1t, now merged, and Sam telling
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today10 * #support. Then Otto's sessions, the record of what he did and spent, and
The Support Specialist agent is Sam now, not Izzy, in its template, the home page tour, the marketing page and the docs, and the tour's Chat sidebar matches the app's11 * last Inky's docs page picking up the change. Otto, Margo, Sam and Inky
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today12 * are agents this workspace hired from role templates; only g1t is built in.
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)13 */
14
15export type Scene = "chat" | "agents" | "code" | "docs";
16
17/** Where the soft cursor points: an element marked `data-tour="<target>"`. */
18export type CursorTarget =
19 | "composer"
20 | "send"
21 | "rail-agents"
22 | "rail-code"
23 | "rail-chat"
24 | "rail-docs"
25 | "task-pull"
26 | "idle";
27
28export type Frame = {
29 scene: Scene;
30 /** Characters of the person's message typed into the composer. */
31 typed: number;
32 /** The person's message has been sent. */
33 sent: boolean;
34 /** g1t shows as typing. */
35 g1tTyping: boolean;
36 /** g1t's handoff reply, with the task card. */
37 handoff: boolean;
38 /** Builder's steps done, 0 to STEPS.length. */
39 steps: number;
40 /** Checks passed on the pull request, 0 to CHECKS.length. */
41 checks: number;
42 /** The diff is shown on the pull request. */
43 diff: boolean;
44 /** @reviewer approved. */
45 approved: boolean;
46 /** The merge queue merged it. */
47 merged: boolean;
48 /** The task card in #web says merged and deploying. */
49 cardMerged: boolean;
50 /** g1t posted that it shipped. */
51 shipped: boolean;
The Support Specialist agent is Sam now, not Izzy, in its template, the home page tour, the marketing page and the docs, and the tour's Chat sidebar matches the app's52 /** Sam says the support team has been told, in #support. */
53 sam: boolean;
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)54 /** Otto consulted Margo, shown as a collapsed line on the task. */
55 consult: boolean;
56 /** The docs page shows the changed line. */
57 docUpdated: boolean;
58 cursor: CursorTarget;
59 /** The cursor is pressing. */
60 click: boolean;
61 /** The cursor is shown at all. */
62 cursorShown: boolean;
63};
64
65/** What the person types into #web. */
66export const ASK = "@g1t the CSV export times out for big accounts. Can we get it fixed before Thursday?";
67
68export const STEPS: { text: string; cost: number }[] = [
69 { text: "Read the export job and the two reports in #web", cost: 0.04 },
70 { text: "Found it: every row loads before the first byte is sent", cost: 0.11 },
71 { text: "Streamed rows in batches of 1,000", cost: 0.19 },
72 { text: "Tested a 200,000-row account: 3.1 s, was 61 s", cost: 0.27 },
73 { text: "Opened pull request #431", cost: 0.31 },
74];
75
76export const CHECKS = ["build", "test", "lint", "preview"];
77
78/** The beats of the story, in ms from the start of the loop. */
79export const BEAT = {
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today80 code: 0,
81 check0: 900,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)82 checkGap: 650,
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today83 diff: 1300,
84 approved: 3900,
85 merged: 5000,
86 railChatClick: 6300,
87 chat: 6450,
88 cardMerged: 7100,
89 shipped: 8400,
The Support Specialist agent is Sam now, not Izzy, in its template, the home page tour, the marketing page and the docs, and the tour's Chat sidebar matches the app's90 sam: 9600,
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today91 railAgentsClick: 11000,
92 agents: 11150,
93 railDocsClick: 15000,
94 docs: 15150,
95 docUpdated: 16300,
96 end: 21000,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)97} as const;
98
99/** The whole loop, in ms. */
100export const LOOP_MS = BEAT.end;
101
102/** The step pills under the frame: where each starts, for jumping to it. */
103export const PILLS: { scene: Scene; label: string; start: number; soon: boolean }[] = [
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today104 { scene: "code", label: "Code", start: 0, soon: false },
105 { scene: "chat", label: "Chat", start: BEAT.chat, soon: false },
One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers106 // Sessions, colleagues brought in, spend against a cap: built.
107 { scene: "agents", label: "Agents", start: BEAT.agents, soon: false },
Docs: a workspace knowledge base people and agents write together108 // Docs is built; an agent keeping a page current after a merge on its
109 // own is not yet, and the line under the frame says so.
110 { scene: "docs", label: "Docs", start: BEAT.docs, soon: false },
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)111];
112
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today113/** Which pill a time falls under. */
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)114export function pillAt(t: number): number {
115 let index = 0;
116 PILLS.forEach((pill, i) => {
117 if (t >= pill.start) index = i;
118 });
119 return index;
120}
121
122/** How far through its pill a time is, 0 to 1. */
123export function pillProgress(t: number): number {
124 const index = pillAt(t);
125 const start = PILLS[index].start;
126 const end = PILLS[index + 1]?.start ?? LOOP_MS;
127 return Math.min(1, Math.max(0, (t - start) / (end - start)));
128}
129
130function count(t: number, first: number, gap: number, max: number): number {
131 if (t < first) return 0;
132 return Math.min(max, Math.floor((t - first) / gap) + 1);
133}
134
135/** Where the cursor is headed, and whether it is pressing, at time t. */
136function cursorAt(t: number): { cursor: CursorTarget; click: boolean } {
137 const press = (at: number) => t >= at && t < at + 180;
138 const moves: [number, CursorTarget][] = [
139 [BEAT.merged + 300, "rail-chat"],
The Support Specialist agent is Sam now, not Izzy, in its template, the home page tour, the marketing page and the docs, and the tour's Chat sidebar matches the app's140 [BEAT.sam + 500, "rail-agents"],
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today141 [BEAT.agents + 2600, "rail-docs"],
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)142 [BEAT.docs + 500, "idle"],
143 ];
144 let cursor: CursorTarget = "idle";
145 for (const [at, target] of moves) if (t >= at) cursor = target;
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today146 const click = press(BEAT.railChatClick) || press(BEAT.railAgentsClick) || press(BEAT.railDocsClick);
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)147 return { cursor, click };
148}
149
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today150/**
151 * The frame at time t (ms into the loop). The ask, g1t's handoff and
152 * Otto's work all happened before the pull request the story opens on, so
153 * they are shown done throughout.
154 */
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)155export function frameAt(time: number): Frame {
156 const t = ((time % LOOP_MS) + LOOP_MS) % LOOP_MS;
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today157 const scene: Scene = t >= BEAT.docs ? "docs" : t >= BEAT.agents ? "agents" : t >= BEAT.chat ? "chat" : "code";
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)158 const { cursor, click } = cursorAt(t);
159 return {
160 scene,
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today161 typed: 0,
162 sent: true,
163 g1tTyping: false,
164 handoff: true,
165 steps: STEPS.length,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)166 checks: count(t, BEAT.check0, BEAT.checkGap, CHECKS.length),
167 diff: t >= BEAT.diff,
168 approved: t >= BEAT.approved,
169 merged: t >= BEAT.merged,
170 cardMerged: t >= BEAT.cardMerged,
171 shipped: t >= BEAT.shipped,
The Support Specialist agent is Sam now, not Izzy, in its template, the home page tour, the marketing page and the docs, and the tour's Chat sidebar matches the app's172 sam: t >= BEAT.sam,
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today173 consult: true,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)174 docUpdated: t >= BEAT.docUpdated,
175 cursor,
176 click,
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today177 cursorShown: t >= BEAT.merged && t < BEAT.end - 600,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)178 };
179}
180
181/** A still frame for each pill, for reduced motion: the moment the step makes its point. */
182export const STILLS: Record<Scene, number> = {
183 code: BEAT.approved + 200,
The Support Specialist agent is Sam now, not Izzy, in its template, the home page tour, the marketing page and the docs, and the tour's Chat sidebar matches the app's184 chat: BEAT.sam + 400,
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today185 agents: BEAT.agents + 400,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)186 docs: BEAT.docUpdated + 400,
187};
188
189/** Whether two frames would draw the same. */
190export function sameFrame(a: Frame, b: Frame): boolean {
191 for (const key of Object.keys(a) as (keyof Frame)[]) if (a[key] !== b[key]) return false;
192 return true;
193}

This file's history is long; its oldest lines are credited to the oldest commit read.