Skip to content
140 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.

Navigation: the workspace's own page with tabs, pins and recent in the sidebar1import assert from "node:assert/strict";
2import { test } from "node:test";
3
g1t is described as the product it is in alpha, and its shell takes the new shape: the README, the docs home and docs/PLAN.md label every feature Live, Preview or Coming; a floating dock with the g1t mark, Today, Chat, Notifications, Agents, Code, Artifacts, your pinned apps and the Apps launcher, with People, Workspace and the account menu (now holding help) at its foot; the workspace's logo and switcher heading a flat sidebar that folds away with Ctrl B, or leading the header's breadcrumbs where there is none; the page in a rounded panel with a full-width header; Today as the front page, with first-time acceptance of agents' pull requests, what needs you and what's at stake, spend today and one item to start with, every number from a service; Notifications in place of the Inbox at /notifications; a bottom bar and More sheet on phones; and sign-in, sign-up, two-factor, reset, invites and choosing a workspace on standalone pages with no app around them.4import { pagePath, underWorkspace, workspacePage, workspaceRedirect } from "./workspace-nav.ts";
Navigation: the workspace's own page with tabs, pins and recent in the sidebar5
Merge branch 'worktree-agent-ad7c6d88d93adc817'6test("a path is one of the workspace's pages, or none", () => {
7 assert.equal(workspacePage("/acme", "acme"), "overview");
8 assert.equal(workspacePage("/Acme/", "acme"), "overview");
9 assert.equal(workspacePage("/acme.data", "acme"), "overview");
10 assert.equal(workspacePage("/acme/-/projects", "acme"), "projects");
11 assert.equal(workspacePage("/acme/-/people", "acme"), "people");
12 assert.equal(workspacePage("/acme/-/packages", "acme"), "packages");
13 assert.equal(workspacePage("/acme/-/teams", "acme"), "teams");
14 assert.equal(workspacePage("/acme/-/insights", "acme"), "insights");
15 // One package or team has a page of its own; settings and the rest have headings.
16 assert.equal(workspacePage("/acme/-/packages/npm/web", "acme"), null);
17 assert.equal(workspacePage("/acme/-/teams/web", "acme"), null);
18 assert.equal(workspacePage("/acme/-/settings", "acme"), null);
19 assert.equal(workspacePage("/acme/-/agents", "acme"), null);
20 assert.equal(workspacePage("/acme/web", "acme"), null);
21 assert.equal(workspacePage("/other", "acme"), null);
Navigation: the workspace's own page with tabs, pins and recent in the sidebar22});
23
24test("old addresses go to where their pages are now", () => {
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.25 // Members and invites is a page of People's again.
26 assert.equal(workspaceRedirect("/acme/-/members"), null);
The front page is Home: where you're needed comes first, however long ago it started, with what's at stake and how long it has waited; then what happened since you were last here, in words, with the agent work that settled, what landed, what deployed, what was decided and what was spent, switchable to the last 24 hours or 7 days; then what's running now. Your last visit is kept with your account, so it's the same on every device; Today's old address leads here. The home guide says how.27 assert.equal(workspaceRedirect("/acme/-/today", "?x=1"), "/acme/-/home?x=1");
28 // Home is the front page itself: not an old address.
29 assert.equal(workspaceRedirect("/acme/-/home"), null);
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)30 // Code's Overview lives at -/overview now: not an old address.
31 assert.equal(workspaceRedirect("/acme/-/overview"), null);
Navigation: the workspace's own page with tabs, pins and recent in the sidebar32 assert.equal(workspaceRedirect("/acme/-/soon/teams"), "/acme/-/teams");
The Marketplace adds what g1t doesn't do and connects what you already use: extensions (Mail, Support, CRM, Recruiting, On-call, and bridges to a helpdesk, CRM or ERP you run, all Soon), starter kits, and integrations connected by the workspace or by each person; agents aren't installed there. Agent templates live in Agents, as starting points you configure, at Agents → Templates, and the Marketplace's old agent addresses lead there. The marketplace and agents guides say how.33 // Agent templates were the Marketplace's Agents tab.
34 assert.equal(workspaceRedirect("/acme/-/marketplace/agents"), "/acme/-/agents/templates");
35 assert.equal(workspaceRedirect("/acme/-/marketplace/agents/qa.data", "?_routes=x"), "/acme/-/agents/templates/qa");
36 assert.equal(workspaceRedirect("/acme/-/marketplace/agentsx"), null);
37 assert.equal(workspaceRedirect("/acme/-/marketplace/extensions"), null);
Navigation: the workspace's own page with tabs, pins and recent in the sidebar38 assert.equal(workspaceRedirect("/acme/-/soon/insights", "?x=1"), "/acme/-/insights?x=1");
39 assert.equal(workspaceRedirect("/acme/-/soon/board"), null);
40 assert.equal(workspaceRedirect("/acme/-/people"), null);
41 assert.equal(workspaceRedirect("/acme/web"), null);
42});
43
Merge branch 'worktree-agent-ad7c6d88d93adc817'44test("?tab= opens that page, keeping the rest of the query", () => {
Navigation: the workspace's own page with tabs, pins and recent in the sidebar45 assert.equal(workspaceRedirect("/acme", "?tab=repositories"), "/acme/-/projects");
46 assert.equal(workspaceRedirect("/acme", "?tab=projects&q=api"), "/acme/-/projects?q=api");
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.47 assert.equal(workspaceRedirect("/acme", "?tab=members"), "/acme/-/members");
48 assert.equal(workspaceRedirect("/acme", "?tab=people"), "/acme/-/people");
Navigation: the workspace's own page with tabs, pins and recent in the sidebar49 assert.equal(workspaceRedirect("/acme", "?tab=overview"), "/acme");
Merge branch 'worktree-agent-ad7c6d88d93adc817'50 assert.equal(workspaceRedirect("/acme", "?tab=settings"), "/acme/-/settings");
Navigation: the workspace's own page with tabs, pins and recent in the sidebar51 assert.equal(workspaceRedirect("/acme", "?tab=nonsense"), null);
52 assert.equal(workspaceRedirect("/acme", ""), null);
53});
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging54
55test("a click's data request is for the same page as a full load", () => {
56 assert.equal(pagePath("/acme/-/insights.data"), "/acme/-/insights");
57 assert.equal(pagePath("/acme/-/insights/"), "/acme/-/insights");
The front page is Home: where you're needed comes first, however long ago it started, with what's at stake and how long it has waited; then what happened since you were last here, in words, with the agent work that settled, what landed, what deployed, what was decided and what was spent, switchable to the last 24 hours or 7 days; then what's running now. Your last visit is kept with your account, so it's the same on every device; Today's old address leads here. The home guide says how.58 assert.equal(workspaceRedirect("/acme/-/today.data", "?_routes=routes%2Fworkspace%2Fmoved-today"), "/acme/-/home");
59 assert.equal(workspaceRedirect("/acme/-/today.data", "?_routes=x&q=ada"), "/acme/-/home?q=ada");
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging60});
Merge branch 'worktree-agent-a8385d293d42c913a'61
62test("an alias or old name leads to the same page under the workspace", () => {
63 assert.equal(underWorkspace("/g1t", "", "flagon-io"), "/flagon-io");
64 assert.equal(underWorkspace("/g1t/g1t/issues", "?q=is%3Aopen", "flagon-io"), "/flagon-io/g1t/issues?q=is%3Aopen");
65 assert.equal(underWorkspace("/g1t/g1t/blob/main/README.md", "", "flagon-io"), "/flagon-io/g1t/blob/main/README.md");
66 assert.equal(underWorkspace("/g1t/-/people/", "", "flagon-io"), "/flagon-io/-/people");
67});
68
69test("a click's data request for an alias leads to the page, not its data", () => {
70 assert.equal(underWorkspace("/g1t.data", "?_routes=routes%2Fworkspace%2Flayout", "flagon-io"), "/flagon-io");
71 assert.equal(
72 underWorkspace("/g1t/g1t/pulls.data", "?_routes=routes%2Frepo%2Flayout&state=closed", "flagon-io"),
73 "/flagon-io/g1t/pulls?state=closed",
74 );
75});
Chat and workspace agents: channels, DMs and named agents you talk to76
The left-hand navigation takes less room and reads at a glance. The dock becomes a rail: a 56-pixel column of icons with the g1t mark on top, the current app marked by a filled square, hover hints naming each with its shortcut, the apps you pinned in a scrolling band, and Apps, People, Workspace and your account at the foot. Each mode's sidebar sits flush against it, 240 pixels wide, opening on Search or jump to and listing its groups under small labels in 36-pixel rows; Ctrl B hides it and the page grows into the room. Every mode's list is built on the shadcn Sidebar now in the kit, so Workspace, Code, People, Notifications, account settings, Chat, Artifacts and Agents all share one frame; the phone keeps its bottom bar and drawer. The workspaces guide describes the rail, the sidebar and the page.77test("the rail's mode follows the address", async () => {
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)78 const { modeOf, modeHome, homePath } = await import("./workspace-nav.ts");
The front page is Home: where you're needed comes first, however long ago it started, with what's at stake and how long it has waited; then what happened since you were last here, in words, with the agent work that settled, what landed, what deployed, what was decided and what was spent, switchable to the last 24 hours or 7 days; then what's running now. Your last visit is kept with your account, so it's the same on every device; Today's old address leads here. The home guide says how.79 assert.equal(modeOf("/", "acme"), "home");
80 assert.equal(modeOf("/acme", "acme"), "home");
81 assert.equal(modeOf("/acme/-/home", "acme"), "home");
Chat controls, public profiles, shadcn selects, and no Docs tab in a project82 // g1t's own public pages light no mode.
83 assert.equal(modeOf("/explore", "acme"), "site");
84 assert.equal(modeOf("/u/ada", "acme"), "site");
85 assert.equal(modeOf("/search.data", "acme"), "site");
g1t is described as the product it is in alpha, and its shell takes the new shape: the README, the docs home and docs/PLAN.md label every feature Live, Preview or Coming; a floating dock with the g1t mark, Today, Chat, Notifications, Agents, Code, Artifacts, your pinned apps and the Apps launcher, with People, Workspace and the account menu (now holding help) at its foot; the workspace's logo and switcher heading a flat sidebar that folds away with Ctrl B, or leading the header's breadcrumbs where there is none; the page in a rounded panel with a full-width header; Today as the front page, with first-time acceptance of agents' pull requests, what needs you and what's at stake, spend today and one item to start with, every number from a service; Notifications in place of the Inbox at /notifications; a bottom bar and More sheet on phones; and sign-in, sign-up, two-factor, reset, invites and choosing a workspace on standalone pages with no app around them.86 assert.equal(modeOf("/notifications", "acme"), "notifications");
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)87 assert.equal(modeOf("/settings/emails", "acme"), "account");
Chat and workspace agents: channels, DMs and named agents you talk to88 assert.equal(modeOf("/acme/-/chat/general", "acme"), "chat");
89 assert.equal(modeOf("/acme/-/chat/dm/c1.data", "acme"), "chat");
90 assert.equal(modeOf("/acme/-/agents", "acme"), "agents");
91 assert.equal(modeOf("/acme/-/agents/ship/profile", "acme"), "agents");
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)92 assert.equal(modeOf("/acme/-/context", "acme"), "agents");
93 assert.equal(modeOf("/acme/-/memory", "acme"), "agents");
Artifacts is a mode in the rail, the shell and the phone's tab bar in place of Docs: its home lists everything you can open by day or as cards under All, Yours and Shared with you, with search, filters and tiles to start a doc (slides, designs and dashboards say they are coming soon), and its sidebar holds favorites, spaces, Private, Shared, projects' docs and the trash.94 assert.equal(modeOf("/acme/-/artifacts", "acme"), "artifacts");
95 assert.equal(modeOf("/acme/-/artifacts/spaces/general", "acme"), "artifacts");
96 // Docs mode is gone: its old addresses are nobody's mode, so they fall to Code's not-found page.
97 assert.equal(modeOf("/acme/-/docs", "acme"), "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)98 assert.equal(modeOf("/acme/-/overview", "acme"), "code");
99 assert.equal(modeOf("/acme/-/projects", "acme"), "code");
100 assert.equal(modeOf("/acme/-/security", "acme"), "code");
101 assert.equal(modeOf("/acme/-/security/settings", "acme"), "workspace");
g1t is described as the product it is in alpha, and its shell takes the new shape: the README, the docs home and docs/PLAN.md label every feature Live, Preview or Coming; a floating dock with the g1t mark, Today, Chat, Notifications, Agents, Code, Artifacts, your pinned apps and the Apps launcher, with People, Workspace and the account menu (now holding help) at its foot; the workspace's logo and switcher heading a flat sidebar that folds away with Ctrl B, or leading the header's breadcrumbs where there is none; the page in a rounded panel with a full-width header; Today as the front page, with first-time acceptance of agents' pull requests, what needs you and what's at stake, spend today and one item to start with, every number from a service; Notifications in place of the Inbox at /notifications; a bottom bar and More sheet on phones; and sign-in, sign-up, two-factor, reset, invites and choosing a workspace on standalone pages with no app around them.102 assert.equal(modeOf("/acme/-/people", "acme"), "people");
103 assert.equal(modeOf("/acme/-/teams/web", "acme"), "people");
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.104 assert.equal(modeOf("/acme/-/people/ana", "acme"), "people");
People lists people. The directory, its counts and its search cover the workspace's people only; agents keep their page under Agents, where the Profile tab now also says what each agent is told about its teams, and the old address of an agent under People sends you there. Agents still join teams beside people: a team's members page groups them under its people with an agent badge, and the org chart shows them as members of the team beside its lead. The people and teams, agents, teams and workspaces guides say so.105 // An agent's old People address (it redirects to Agents) is still read as People's until it does.
People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how.106 assert.equal(modeOf("/acme/-/people/agents/margo", "acme"), "people");
107 assert.equal(modeOf("/acme/-/org-chart", "acme"), "people");
108 assert.equal(modeOf("/acme/-/members", "acme"), "people");
g1t is described as the product it is in alpha, and its shell takes the new shape: the README, the docs home and docs/PLAN.md label every feature Live, Preview or Coming; a floating dock with the g1t mark, Today, Chat, Notifications, Agents, Code, Artifacts, your pinned apps and the Apps launcher, with People, Workspace and the account menu (now holding help) at its foot; the workspace's logo and switcher heading a flat sidebar that folds away with Ctrl B, or leading the header's breadcrumbs where there is none; the page in a rounded panel with a full-width header; Today as the front page, with first-time acceptance of agents' pull requests, what needs you and what's at stake, spend today and one item to start with, every number from a service; Notifications in place of the Inbox at /notifications; a bottom bar and More sheet on phones; and sign-in, sign-up, two-factor, reset, invites and choosing a workspace on standalone pages with no app around them.109 assert.equal(modeOf("/acme/-/apps", "acme"), "apps");
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)110 assert.equal(modeOf("/acme/-/billing", "acme"), "workspace");
111 assert.equal(modeOf("/acme/-/tokens", "acme"), "workspace");
112 assert.equal(modeOf("/acme/-/workspace", "acme"), "workspace");
Chat and workspace agents: channels, DMs and named agents you talk to113 assert.equal(modeOf("/acme/web/pulls", "acme"), "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)114 assert.equal(modeOf("/new", "acme"), "code");
115 // Another workspace's chat is not this one's mode; its repositories are Code's.
Chat and workspace agents: channels, DMs and named agents you talk to116 assert.equal(modeOf("/other/-/chat", "acme"), "code");
The front page is Home: where you're needed comes first, however long ago it started, with what's at stake and how long it has waited; then what happened since you were last here, in words, with the agent work that settled, what landed, what deployed, what was decided and what was spent, switchable to the last 24 hours or 7 days; then what's running now. Your last visit is kept with your account, so it's the same on every device; Today's old address leads here. The home guide says how.117 assert.equal(modeHome("home", "acme"), "/acme/-/home");
g1t is described as the product it is in alpha, and its shell takes the new shape: the README, the docs home and docs/PLAN.md label every feature Live, Preview or Coming; a floating dock with the g1t mark, Today, Chat, Notifications, Agents, Code, Artifacts, your pinned apps and the Apps launcher, with People, Workspace and the account menu (now holding help) at its foot; the workspace's logo and switcher heading a flat sidebar that folds away with Ctrl B, or leading the header's breadcrumbs where there is none; the page in a rounded panel with a full-width header; Today as the front page, with first-time acceptance of agents' pull requests, what needs you and what's at stake, spend today and one item to start with, every number from a service; Notifications in place of the Inbox at /notifications; a bottom bar and More sheet on phones; and sign-in, sign-up, two-factor, reset, invites and choosing a workspace on standalone pages with no app around them.118 assert.equal(modeHome("notifications", "acme"), "/notifications");
119 assert.equal(modeHome("people", "acme"), "/acme/-/people");
120 assert.equal(modeHome("apps", "acme"), "/acme/-/apps");
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)121 assert.equal(modeHome("code", "acme"), "/acme/-/overview");
122 assert.equal(modeHome("workspace", "acme"), "/acme/-/workspace");
The front page is Home: where you're needed comes first, however long ago it started, with what's at stake and how long it has waited; then what happened since you were last here, in words, with the agent work that settled, what landed, what deployed, what was decided and what was spent, switchable to the last 24 hours or 7 days; then what's running now. Your last visit is kept with your account, so it's the same on every device; Today's old address leads here. The home guide says how.123 assert.equal(homePath("acme"), "/acme/-/home");
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)124 assert.equal(homePath("acme", "?agent=new"), "/acme/-/overview?agent=new");
125 assert.equal(homePath("acme", "?tab=landed&_routes=x"), "/acme/-/overview?tab=landed");
Chat and workspace agents: channels, DMs and named agents you talk to126});
127
128test("a member without Code access is sent around Code's pages", async () => {
129 const { codeGate } = await import("./workspace-nav.ts");
130 const none = ["acme"];
The front page is Home: where you're needed comes first, however long ago it started, with what's at stake and how long it has waited; then what happened since you were last here, in words, with the agent work that settled, what landed, what deployed, what was decided and what was spent, switchable to the last 24 hours or 7 days; then what's running now. Your last visit is kept with your account, so it's the same on every device; Today's old address leads here. The home guide says how.131 assert.equal(codeGate("/", "", none, "acme"), "/acme/-/home");
Chat and workspace agents: channels, DMs and named agents you talk to132 assert.equal(codeGate("/", "", none, "other"), null);
The front page is Home: where you're needed comes first, however long ago it started, with what's at stake and how long it has waited; then what happened since you were last here, in words, with the agent work that settled, what landed, what deployed, what was decided and what was spent, switchable to the last 24 hours or 7 days; then what's running now. Your last visit is kept with your account, so it's the same on every device; Today's old address leads here. The home guide says how.133 assert.equal(codeGate("/acme", "", none, "acme"), "/acme/-/home");
Chat and workspace agents: channels, DMs and named agents you talk to134 assert.equal(codeGate("/acme/web/pull/3", "?x=1", none, "acme"), "/acme/-/code-access?from=%2Facme%2Fweb%2Fpull%2F3%3Fx%3D1");
135 assert.equal(codeGate("/acme/-/projects", "", none, "acme"), "/acme/-/code-access?from=%2Facme%2F-%2Fprojects");
136 assert.equal(codeGate("/acme/-/chat/general", "", none, "acme"), null);
137 assert.equal(codeGate("/acme/-/code-access", "", none, "acme"), null);
138 assert.equal(codeGate("/other/web", "", none, "acme"), null);
139 assert.equal(codeGate("/acme/web", "", [], "acme"), null);
140});

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