Skip to content
1,485 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.

Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1import {
2 Activity as ActivityIcon,
3 ArrowRight,
4 ArrowUpRight,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb975 BookOpen,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API6 Bot,
7 Brain,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb978 Globe,
9 Tag,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API10 Code2,
11 GitBranch,
12 GitCommitHorizontal,
13 GitMerge,
14 Hand,
15 HeartPulse,
16 Loader2,
17 Lock,
A project is an app or a library: libraries show their package and how to ship a release, not production18 Package as PackageGlyph,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API19 Pin,
20 Rocket,
21 RotateCw,
22} from "lucide-react";
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 2523import { waitUntil } from "cloudflare:workers";
24import { isbot } from "isbot";
Fast pages, required checks on the branch, self-hosted runners, honest incidents25import { type ReactNode, Suspense } from "react";
Merge branch 'worktree-agent-a985c01f43538af01'26import { Await, Form, Link } from "react-router";
Projects: what a workspace builds and runs, first on every page27
A project is an app or a library: libraries show their package and how to ship a release, not production28import {
29 type AgentRun,
30 type G1tEvent,
31 type Memory,
32 type PackageSummary,
33 type Project,
34 type Pull,
35 RUN_KIND_LABEL,
36 isActiveRun,
37 workOwner,
38} from "@g1t/contracts";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API39
Projects: what a workspace builds and runs, first on every page40import type { Route } from "./+types/overview";
Usage, Billing settings and prepaid AI credit; fixes from the UX audit41import { InlineMarkdown } from "../../components/inline-markdown";
Merge branch 'site-fixes' into fast-push42import { DeployLink, host, StatusDot } from "../../components/deploy";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API43import { CheckBadge } from "../../components/checks";
44import { Elapsed, formatCost, useLiveRefresh } from "../../components/agents";
45import { ActivityFeed, DeployStrip, Meter, NeedsList, Panel, Quiet, Unavailable, percent } from "../../components/mission";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look46import { type ActiveBranch, ActiveBranches } from "../../components/branches";
47import { ProductionChecklist } from "../../components/checklist";
A project is an app or a library: libraries show their package and how to ship a release, not production48import { PackageIcon } from "../../components/package-icon";
Merge branch 'worktree-agent-ae1299e92e4462012'49import { Hint } from "../../components/ui/hint";
Merge branch 'worktree-agent-aca4790ea223aa235'50import { Loading, Skeleton, SkeletonRows } from "../../components/ui/skeleton";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look51import { ProductionShot } from "../../components/production-shot";
52import { GithubLinkStrip } from "../../components/github";
Merge branch 'mirroring' into artifacts-mode53import { MirrorOverviewNote, useRepoMirror } from "../../components/mirror";
Usage, Billing settings and prepaid AI credit; fixes from the UX audit54import { distinctFacts } from "../../lib/memory-facts";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look55import { githubApp } from "../../lib/github.server";
Merge branch 'worktree-agent-a985c01f43538af01'56import { Avatar, ButtonLink, CopyLine, SubmitButton, TimeAgo } from "../../components/ui";
Overview: Recent changes show the avatar of whoever the line names, and every name opens its card57import { ChangeSize, PersonLink } from "../../components/work";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API58import {
59 type ActivityItem,
60 type Need,
61 type PipelineStage,
62 PIPELINE,
63 TIME,
Site: a project's activity names people, not their account ids64 actorIds,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API65 ageBuckets,
66 firstPassRate,
67 groupActivity,
Site: a project's activity names people, not their account ids68 nameActor,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API69 passRate,
70 pipelineStage,
Merge demo dry run fixes: settled checks, other attempts, queue links, clone box, activity feed, DEMO.md71 PROJECT_FEED_EVENT_TYPES,
72 projectFeed,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API73 queuedNumbers,
74 rankNeeds,
Merge branch 'browser-pass' into fast-push75 runHealth,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API76 stuckMinutes,
77} from "../../lib/mission";
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9778import { agentWasAssigned, checklistPlan, hasInstructions, productionChecklist, releaseChecklist, startChecklist } from "../../lib/checklist";
A project is an app or a library: libraries show their package and how to ship a release, not production79import { ECOSYSTEM_LABEL, installCommands } from "../../lib/packages";
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9780import {
81 PUBLISH_GUIDES,
82 bare,
83 hasRelease,
84 kindLabel,
85 linksToShow,
86 latestTag,
87 libraryPackages,
88 packageName,
89 packagePath,
90 projectChanges,
91 publishGuide,
92} from "../../lib/project-kind";
93import { AboutEditor, KindMenu, LinkList } from "../../components/project-about";
94import { DocsHead, ElsewhereHead, type ExternalDeployment, OtherHead, WhereItRuns } from "../../components/project-head";
Site: a project's activity names people, not their account ids95import { actions, agents, deployments, events as eventLog, identity, packages, projects, repos, work } from "../../lib/services.server";
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent96import { madeByG1t } from "../../lib/opened-by";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look97import { assertSameOrigin, getViewer, requireUser } from "../../lib/session.server";
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 2598import { accessTo, countsFor, projectFor, refusal, repoFor } from "../../lib/access.server";
Production thumbnails follow each deploy, not each commit, and old ones are cleared daily99import { shotVersion } from "./production-screenshot";
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97100import { DeploymentsPanel } from "../../components/deployments-panel";
101import { environmentUrl, productionEnvironment } from "../../lib/deployments";
Merge branch 'worktree-agent-aaf03bdceac799c89'102import { cloneUrl, useAddresses } from "../../lib/addresses";
Branches and Tags pages, each file's last commit, and the branch menu on files103import { readBranches } from "../../lib/branches.server";
Merge checks: statuses and check runs on every commit104import { commitChecksFor } from "../../lib/commit-checks.server";
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)105import { showCommits } from "../../lib/commit-people.server";
106import { CommitAvatars, CommitNames } from "../../components/commit-person";
Merge checks: statuses and check runs on every commit107import { CommitChecksBadge } from "../../components/commit-checks";
Projects: what a workspace builds and runs, first on every page108
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API109const MAX_LANDED = 6;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look110/** Branches read for the Active branches list, and shown. */
111const BRANCHES_READ = 10;
112const BRANCHES_SHOWN = 5;
A project is an app or a library: libraries show their package and how to ship a release, not production113/** A library's packages shown on its overview; the rest are a link away. */
114const PACKAGES_SHOWN = 3;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97115/** The default branch's latest commits listed. */
116const COMMITS_SHOWN = 5;
117/** How long Active branches may take before the section links to Branches instead. */
118const BRANCHES_WAIT_MS = 3_500;
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25119/**
120 * The same for a crawler, which gets the page only once everything in it
121 * has settled (entry.server.tsx): past this, it gets the link to Branches.
122 */
123const BRANCHES_WAIT_CRAWLER_MS = 700;
Projects: what a workspace builds and runs, first on every page124
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily125/**
126 * The overview streams: the layout's header and tabs (one repository
127 * lookup, one project) go out at once with a skeleton below them, and
128 * everything here, seventeen calls across six services, follows in the
129 * same response as it settles. Crawlers wait for all of it
130 * (entry.server.tsx). Before, the first byte waited on the slowest of them.
131 */
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25132export function loader({ params, context, request }: Route.LoaderArgs) {
133 const userAgent = request.headers.get("user-agent");
134 return { overview: overviewData({ params, context }, Boolean(userAgent && isbot(userAgent))) };
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily135}
136
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25137async function overviewData({ params, context }: Pick<Route.LoaderArgs, "params" | "context">, crawler: boolean) {
Projects: what a workspace builds and runs, first on every page138 const viewer = getViewer(context);
139 const path = { namespace: params.owner, name: params.repo };
140 const ref = { workspace: params.owner, slug: params.repo };
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API141 const now = Date.now();
142 // A section whose service fails shows its own empty state; the page stays up.
143 const soft = <T,>(promise: Promise<T> | null): Promise<T | null> =>
144 promise ? promise.catch((error) => (console.warn("overview:", error), null)) : Promise.resolve(null);
Fast pages, required checks on the branch, self-hosted runners, honest incidents145 // People with a role of their own here see its running parts: deployments,
146 // memory, setup; everyone who can read it sees the rest. Not awaited: only
147 // those sections wait on the repository lookup, which the layout makes in
148 // this request too.
149 const accessP = accessTo(context, params);
150 const memberP = accessP.then((access) => access.insider);
151 const forMembers = <T,>(start: () => Promise<T>): Promise<T | null> =>
152 memberP.then((member) => (member ? soft(start()) : null));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look153 const repoP = soft(repoFor(context, params));
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25154 const projectP = soft(projectFor(context, params));
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97155 // A library or a tool shows its packages where an app shows production,
156 // and every project g1t does not deploy counts a workflow in its checklist.
157 // Deployments from anywhere (g1t.page, g1t Actions, the API), by environment, for anyone who can read it.
158 const environmentsP = projectP.then((found) =>
159 soft(deployments.environments(found?.ok && found.value.source.kind === "hosted" ? found.value.source.repo : path, viewer)),
A project is an app or a library: libraries show their package and how to ship a release, not production160 );
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97161 const planP = projectP.then((project) => (project?.ok ? checklistPlan(project.value).plan : null));
162 const libraryRepoP = Promise.all([planP, repoP]).then(([plan, repo]) => (plan === "release" && repo?.ok ? repo.value : null));
A project is an app or a library: libraries show their package and how to ship a release, not production163 const packagesP = libraryRepoP.then((repo) => (repo ? soft(packages.list(params.owner, viewer, { repoId: repo.id })) : null));
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97164 const workflowsP = planP.then((plan) => (plan && plan !== "production" ? forMembers(() => actions.workflows(path, viewer)) : null));
Merge branch 'browser-pass' into fast-push165 // Recent workflow runs, for Health when no pull request's checks have finished lately.
166 const workflowRunsP = soft(actions.runs(path, viewer, { limit: 30 }));
Merge demo dry run fixes: settled checks, other attempts, queue links, clone box, activity feed, DEMO.md167 // Only the kinds the feed shows: the newest events are mostly session
168 // steps and merge checks, which would otherwise crowd out everything.
169 const eventsP = repoP.then((repo) =>
170 repo?.ok ? soft(eventLog.list({ repoId: repo.value.id, types: [...PROJECT_FEED_EVENT_TYPES], limit: 150 })) : null,
171 );
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API172 const minePullsP = repoP.then((repo) =>
173 repo?.ok && viewer
174 ? soft(work.listActivePulls(viewer)).then((list) => (list ?? []).filter((item) => item.pull.repoId === repo.value.id))
175 : null,
176 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look177 const openP = soft(work.listPulls(path, viewer, "open"));
178 // Where its code came from on GitHub, for members.
Fast pages, required checks on the branch, self-hosted runners, honest incidents179 const githubP = repoP.then((repo) => (repo?.ok ? forMembers(() => githubApp.link(repo.value.id)) : null));
180 const listP = forMembers(() => deployments.list(ref, viewer));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look181 // Active branches: the newest few besides the default, with how far each
182 // has moved. Branches with an open pull request are read first.
183 const branchesP = Promise.all([repoP, soft(repos.branches(path, viewer)), openP, listP]).then(
184 async ([repo, branchList, pulls, deploys]): Promise<{ main: string; total: number; shown: ActiveBranch[] } | null> => {
185 if (!repo?.ok || !branchList?.ok) return null;
186 const main = repo.value.defaultBranch;
Branches and Tags pages, each file's last commit, and the branch menu on files187 if (branchList.value.every((branch) => branch.name === main)) return { main, total: 0, shown: [] };
188 const read = await readBranches(
189 path,
190 viewer,
191 {
192 defaultBranch: main,
193 branches: branchList.value,
194 pulls: pulls?.ok ? pulls.value : [],
195 previews: deploys?.ok ? deploys.value.live.filter((app) => app.kind === "preview") : [],
196 },
197 BRANCHES_READ,
198 );
199 return { main, total: read.total, shown: read.shown.slice(0, BRANCHES_SHOWN) };
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look200 },
201 );
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25202 // Active branches walk history when a branch or the default branch has
203 // moved: streamed, so the rest shows first. Bounded well inside the
204 // response's stream timeout (entry.server.tsx): a promise still pending
205 // when the stream ends never settles in the browser, and its skeleton
206 // would stay. The walk finishes after the page if it must (waitUntil),
207 // so repos keeps the answer and the next view has it. Crawlers, which
208 // wait for the whole page, wait for it only briefly.
209 waitUntil(branchesP.then(() => undefined, () => undefined));
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97210 const branches = Promise.race([
211 branchesP.catch(() => null),
Merge project overview: one branch_drift call, spliced histories, cached tags, 6 repos calls instead of 25212 new Promise<"slow">((resolve) => setTimeout(() => resolve("slow"), crawler ? BRANCHES_WAIT_CRAWLER_MS : BRANCHES_WAIT_MS)),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97213 ]);
Projects no longer depend on each other: the dependsOn relation, its settings page (old links redirect), the overview card, a pull request's Affects panel and preview stacks, reference variables and agents' notes on what a project uses are gone; the table is dropped in a later deploy214 const [{ insider: member, can }, project, settings, list, open, closed, log, counts, runs, queue, issues, memories, recent, mine, domains, root, packageList, workflows, tagList] = await Promise.all([
Fast pages, required checks on the branch, self-hosted runners, honest incidents215 accessP,
A project is an app or a library: libraries show their package and how to ship a release, not production216 projectP,
Fast pages, required checks on the branch, self-hosted runners, honest incidents217 forMembers(() => deployments.settings(ref, viewer)),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look218 listP,
219 openP,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API220 soft(work.listPulls(path, viewer, "closed")),
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)221 // With their people, in one identity call.
222 soft(repos.log(path, viewer, null, COMMITS_SHOWN).then(async (found) => (found.ok ? { ok: true as const, value: await showCommits(found.value) } : found))),
Fast pages, required checks on the branch, self-hosted runners, honest incidents223 soft(countsFor(context, params)),
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API224 soft(agents.listRuns(viewer, { repo: path, limit: 60 })),
225 soft(work.queue(path, viewer)),
226 soft(work.listIssues(path, viewer, { state: "open" })),
Fast pages, required checks on the branch, self-hosted runners, honest incidents227 forMembers(() => agents.listMemories(viewer, params.owner, path)),
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API228 eventsP,
229 minePullsP,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look230 // For the checklist, which only members see.
Fast pages, required checks on the branch, self-hosted runners, honest incidents231 forMembers(() => deployments.domains(ref, viewer)),
232 forMembers(() => repos.tree(path, viewer, null, "")),
A project is an app or a library: libraries show their package and how to ship a release, not production233 packagesP,
234 workflowsP,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97235 // The latest release, for About.
236 soft(repos.tags(path, viewer)),
Projects: what a workspace builds and runs, first on every page237 ]);
Merge branch 'browser-pass' into fast-push238 const workflowRuns = await workflowRunsP;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API239 const ok = <T,>(result: { ok: true; value: T } | { ok: false } | null): T | null => (result?.ok ? result.value : null);
240
241 const openPulls = ok(open) ?? [];
242 const runList = ok(runs) ?? [];
243 const live = runList.filter((run) => isActiveRun(run.status));
244 const builds = ok(list)?.deployments ?? [];
245 const landed = (ok(closed) ?? [])
246 .filter((pull) => pull.status === "merged")
247 .sort((a, b) => Date.parse(b.mergedAt ?? b.updatedAt) - Date.parse(a.mergedAt ?? a.updatedAt));
248 const queued = queuedNumbers(ok(queue)?.active ?? []);
249 const runOn = new Map<number, AgentRun>();
250 for (const run of live) if (run.number != null && !runOn.has(run.number)) runOn.set(run.number, run);
251 const lifecycleOf = new Map((mine ?? []).map((item) => [item.pull.number, item.lifecycle]));
252
253 // --- The pipeline -----------------------------------------------------------
254 type Card = {
255 number: number;
256 title: string;
257 agent: string;
258 status: Pull["status"];
259 checkStatus: Pull["checkStatus"];
260 step: string | null;
261 runKind: string | null;
262 runStarted: string | null;
263 needsYou: boolean;
264 at: string;
265 };
266 const columns: Record<PipelineStage, Card[]> = { working: [], checking: [], reviewing: [], queue: [], landed: [] };
267 for (const pull of openPulls.slice(0, 40)) {
268 const run = runOn.get(pull.number);
Fast pages, required checks on the branch, self-hosted runners, honest incidents269 columns[pipelineStage(pull, run, queued, lifecycleOf.get(pull.number))].push({
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API270 number: pull.number,
271 title: pull.title,
272 agent: pull.agent,
273 status: pull.status,
274 checkStatus: pull.checkStatus,
275 step: run?.step ?? null,
276 runKind: run ? RUN_KIND_LABEL[run.kind] : null,
277 runStarted: run ? (run.startedAt ?? run.createdAt) : null,
278 needsYou: lifecycleOf.get(pull.number)?.stage === "needs_you",
279 at: pull.updatedAt,
280 });
281 }
282 for (const pull of landed.filter((p) => now - Date.parse(p.mergedAt ?? p.updatedAt) < 3 * TIME.DAY).slice(0, 5)) {
283 columns.landed.push({
284 number: pull.number,
285 title: pull.title,
286 agent: pull.agent,
287 status: pull.status,
288 checkStatus: pull.checkStatus,
289 step: null,
290 runKind: null,
291 runStarted: null,
292 needsYou: false,
293 at: pull.mergedAt ?? pull.updatedAt,
294 });
295 }
296
297 // --- Needs you ---------------------------------------------------------------
298 const base = `/${params.owner}/${params.repo}`;
299 const needs: Need[] = [];
300 const latestProduction = builds.find((build) => build.kind === "production") ?? null;
301 if (latestProduction?.status === "failed") {
302 needs.push({
303 key: "deploy",
304 kind: "deploy",
305 title: "The production build failed",
306 detail: latestProduction.error ?? "Production still serves the build before it.",
307 to: `${base}/deployments/${latestProduction.id}`,
308 action: "See the build",
309 at: Date.parse(latestProduction.finishedAt ?? latestProduction.createdAt),
310 where: latestProduction.commit.slice(0, 7),
311 });
312 }
313 const me = viewer?.username.toLowerCase();
314 for (const pull of openPulls) {
315 const to = `${base}/pull/${pull.number}`;
316 const where = `#${pull.number}`;
317 const lifecycle = lifecycleOf.get(pull.number);
318 if (lifecycle?.stage === "needs_you") {
319 const conflict = /conflict/i.test(lifecycle.detail);
320 needs.push({ key: `pull:${pull.number}`, kind: conflict ? "conflict" : "stalled", title: pull.title, detail: lifecycle.detail, to, action: conflict ? "Resolve" : "Decide", at: Date.parse(pull.updatedAt), where });
321 } else if (lifecycle?.stage === "ready") {
322 needs.push({ key: `pull:${pull.number}`, kind: "ready", title: pull.title, detail: "Ready to land when you merge it.", to, action: "Merge", at: Date.parse(pull.updatedAt), where });
323 }
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights324 // Never your own, nor one g1t made for you; whoever it is for asked.
325 const owner = workOwner(pull).username;
326 if (me && pull.status === "open" && owner.toLowerCase() !== me && pull.reviewers.some((name) => name.toLowerCase() === me)) {
327 needs.push({ key: `review:${pull.number}`, kind: "review", title: pull.title, detail: `${owner} asked for your review.`, to, action: "Review", at: Date.parse(pull.updatedAt), where });
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API328 }
329 if (member && pull.status === "open" && pull.checkStatus === "failed" && !runOn.has(pull.number) && !lifecycle) {
Fast pages, required checks on the branch, self-hosted runners, honest incidents330 needs.push({ key: `checks:${pull.number}`, kind: "checks", title: pull.title, detail: "It failed in the merge queue and no agent is fixing it.", to, action: "See checks", at: Date.parse(pull.updatedAt), where });
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API331 }
332 }
333 for (const run of live) {
334 const minutes = stuckMinutes(run, now);
335 if (minutes == null) continue;
336 needs.push({
337 key: `run:${run.id}`,
338 kind: "stuck",
339 title: run.title ?? `${run.agent}'s run`,
340 detail: `${run.agent} has reported nothing for ${minutes} min${run.step ? `. Last: ${run.step}` : ""}.`,
341 to: `${base}/agents/runs/${run.id}`,
342 action: "Look",
343 at: Date.parse(run.updatedAt),
344 where: run.number != null ? `#${run.number}` : null,
345 });
346 }
347
348 // --- Activity and health -------------------------------------------------------
349 const eventList: G1tEvent[] = recent ?? [];
Merge demo dry run fixes: settled checks, other attempts, queue links, clone box, activity feed, DEMO.md350 const logged: ActivityItem[] = projectFeed(eventList, path);
Site: a project's activity names people, not their account ids351 // The log names people by account id: their usernames, in one lookup.
352 const ids = actorIds(logged.map((item) => item.actor));
353 const names = ids.length > 0 ? await soft(identity.usernames(ids)) : {};
354 const items = logged.map((item) => ({ ...item, actor: nameActor(item.actor, names) }));
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API355 const checkEvents = eventList.flatMap((event) =>
356 event.type === "checks.completed" ? [{ repo: "", number: event.data.number, at: Date.parse(event.time), passed: event.data.status === "passed" }] : [],
357 );
358 const memoryList = ok(memories)?.project ?? [];
Usage, Billing settings and prepaid AI credit; fixes from the UX audit359 // Pinned first, then the newest; a fact remembered twice, once.
360 const knows = distinctFacts([...memoryList.filter((m) => m.pinned), ...memoryList.filter((m) => !m.pinned).sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt))]).slice(0, 5);
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API361 const openIssues = ok(issues);
362
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look363 // --- Getting to production ----------------------------------------------------
364 const wentLive = (status: string) => status === "ready" || status === "replaced" || status === "down";
365 const liveApps = ok(list)?.live ?? [];
366 const commitNow = ok(log)?.[0] ?? null;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97367 // Production deployed somewhere other than g1t.page (by g1t Actions, or
368 // reported through the API): the slot an app deployed elsewhere shows.
369 const environments = await environmentsP;
370 const environmentList = environments?.ok ? environments.value : null;
371 const reported = productionEnvironment(environmentList?.environments ?? []);
372 const deployment: ExternalDeployment | null =
373 reported?.latest && reported.latest.source !== "g1t_page"
374 ? {
375 environment: reported.name,
376 url: environmentUrl(reported),
377 state: reported.latest.state,
378 sha: reported.latest.sha,
379 created_at: reported.latest.updated_at,
380 }
381 : null;
382 // An app that has not said where it runs, but reports production from
383 // elsewhere, runs elsewhere.
384 const found = ok(project);
385 const projectValue = found && deployment && found.kind === "app" && found.runs == null ? { ...found, runs: "elsewhere" as const } : found;
386 const plan = projectValue ? checklistPlan(projectValue) : { plan: "production" as const, title: "Get to production" };
387 const isLibrary = plan.plan === "release";
A project is an app or a library: libraries show their package and how to ship a release, not production388 const published = libraryPackages(ok(packageList) ?? []);
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97389 const hasWorkflow = workflows?.ok ? workflows.value.length > 0 : null;
A project is an app or a library: libraries show their package and how to ship a release, not production390 const firstSteps = {
391 base,
392 hasCode: commitNow != null || projectValue?.source.kind === "mirror",
393 instructions: commitNow == null ? false : root?.ok ? hasInstructions(root.value.entries.map((entry) => entry.name)) : null,
394 agentAssigned: agentWasAssigned({
395 runAgents: runList.map((run) => run.agent),
396 pullAgents: [...openPulls, ...(ok(closed) ?? [])].map((pull) => pull.agent),
397 issues: openIssues ?? [],
398 }),
399 };
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97400 const links = projectValue?.links;
A project is an app or a library: libraries show their package and how to ship a release, not production401 const checklist = !member
402 ? null
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97403 : plan.plan !== "production" && plan.plan !== "release"
404 ? startChecklist(plan.plan, {
405 ...firstSteps,
406 hasWorkflow,
407 productionUrl: projectValue?.productionUrl ?? null,
408 hasLinks: !!links && (links.homepage != null || links.docs != null || links.custom.length > 0),
409 hasDocsLink: !!links && (links.docs != null || links.homepage != null),
410 })
A project is an app or a library: libraries show their package and how to ship a release, not production411 : isLibrary
412 ? releaseChecklist({
413 ...firstSteps,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97414 hasWorkflow,
A project is an app or a library: libraries show their package and how to ship a release, not production415 released: hasRelease(published),
416 releaseTo: published[0]
417 ? packagePath(published[0])
418 : (publishGuide(projectValue?.ecosystem ?? null)?.guide ?? "https://docs.g1t.sh/guides/packages/"),
419 })
420 : productionChecklist({
421 ...firstSteps,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look422 deploysEnabled: ok(settings)?.enabled ?? false,
423 productionDeployed:
424 liveApps.some((app) => app.kind === "production") ||
425 builds.some((build) => build.kind === "production" && wentLive(build.status)),
426 domains: ok(domains)?.domains.length ?? null,
427 previewOpened:
428 liveApps.some((app) => app.kind === "preview") || builds.some((build) => build.kind === "preview" && wentLive(build.status)),
A project is an app or a library: libraries show their package and how to ship a release, not production429 });
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look430
Merge checks: statuses and check runs on every commit431 // The checks on the latest commit and each active branch's head, in
432 // one call once the branches are read: streamed in beside them.
433 const checks = branches.then((found) =>
434 commitChecksFor(path, viewer, [commitNow?.hash, ...(found && found !== "slow" ? found.shown : []).map((branch) => branch.commit?.hash)]),
435 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look436 const github = await githubP;
Projects: what a workspace builds and runs, first on every page437 return {
438 member,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look439 can,
440 github,
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97441 project: projectValue,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API442 settings: ok(settings),
443 builds: builds.slice(0, 30),
444 live: ok(list)?.live ?? [],
445 commit: ok(log)?.[0] ?? null,
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)446 commits: (ok(log) ?? []).map((one) => ({ hash: one.hash, message: one.message.split("\n")[0] ?? "", author: one.author, coAuthors: one.coAuthors, at: one.authoredAt })),
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97447 release: latestTag(ok(tagList) ?? []),
448 checklistTitle: plan.title,
449 // Production as reported from outside g1t, for an app deployed elsewhere.
450 deployment,
451 // Every environment's latest deployment, for the Deployments panel.
452 environments: environmentList,
453 // About's languages and contributors, filled in by the files' About work.
454 languages: null as { name: string; share: number }[] | null,
455 contributors: null as { username: string }[] | null,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API456 open: ok(counts) ?? { issues: openIssues?.length ?? 0, pulls: openPulls.length },
457 agentsLive: live.slice(0, 6),
458 runsLoaded: runs?.ok ?? false,
459 pullsLoaded: open?.ok ?? false,
460 columns,
461 needs: rankNeeds(needs),
462 landed: landed.slice(0, MAX_LANDED),
463 groups: groupActivity(items).slice(0, 30),
464 eventsLoaded: recent != null,
465 health: {
Merge branch 'browser-pass' into fast-push466 // A pull request's checks when any finished lately; otherwise the
467 // project's workflow runs, for one that ships straight to its branch.
468 ...(checkEvents.length > 0 || !workflowRuns?.ok
469 ? { passRate: passRate(checkEvents.map((c) => c.passed)), firstPass: firstPassRate(checkEvents), checkRuns: checkEvents.length }
470 : runHealth(workflowRuns.value)),
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API471 ages: openIssues ? ageBuckets(openIssues.map((issue) => issue.createdAt), now) : null,
472 },
473 knows: knows as Memory[],
474 memoryCount: memoryList.length,
475 memoriesLoaded: memories?.ok ?? false,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look476 checklist,
A project is an app or a library: libraries show their package and how to ship a release, not production477 library: isLibrary
478 ? { packages: published.slice(0, PACKAGES_SHOWN), total: published.length, loaded: packageList?.ok ?? false }
479 : null,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look480 branches,
Merge checks: statuses and check runs on every commit481 checks,
Projects: what a workspace builds and runs, first on every page482 };
483}
484
485export async function action({ request, params, context }: Route.ActionArgs) {
486 assertSameOrigin(request);
487 const user = requireUser(context, request);
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look488 const form = await request.formData().catch(() => null);
489 const intent = form?.get("intent");
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97490 // What it is, where it runs, its description and links: its settings.
491 if (form && (intent === "kind" || intent === "about")) {
492 const refused = await refusal(context, params, "manage_settings");
493 if (refused) return { error: refused };
494 const saved = await projects.update(user, params.owner, params.repo, projectChanges(form));
495 return saved.ok ? { notice: "Saved." } : { error: saved.error.message };
496 }
Merge branch 'mirroring' into artifacts-mode497 // Deploying is compute. Syncing with GitHub is under Settings → Mirroring.
498 const refused = await refusal(context, params, "run");
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look499 if (refused) return { error: refused };
Projects: what a workspace builds and runs, first on every page500 const started = await deployments.redeploy(user, { workspace: params.owner, slug: params.repo }, null);
501 return started.ok ? { notice: "Production is building." } : { error: started.error.message };
502}
503
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily504type Loaded = Awaited<ReturnType<typeof overviewData>>;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API505type Card = Loaded["columns"]["working"][number];
506
507const STAGE_TONE: Record<PipelineStage, string> = {
Merge branch 'worktree-agent-ad7c6d88d93adc817'508 working: "bg-accent",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API509 checking: "bg-info",
510 reviewing: "bg-warn",
Merge branch 'worktree-agent-ad7c6d88d93adc817'511 queue: "bg-success-dim",
512 landed: "bg-success",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API513};
514
515function PipelineCard({ card, base, stage }: { card: Card; base: string; stage: PipelineStage }) {
516 return (
517 <li>
518 <Link
519 to={`${base}/pull/${card.number}`}
520 prefetch="intent"
521 className={`block rounded-lg border bg-bg/60 p-2.5 transition-colors hover:border-line-strong hover:bg-raised ${
Merge branch 'worktree-agent-ad7c6d88d93adc817'522 card.needsYou ? "border-warn/50" : card.step ? "border-accent/30" : "border-line"
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API523 }`}
524 >
525 <span className="line-clamp-2 text-[0.8125rem] leading-snug font-medium">{card.title}</span>
526 <span className="mt-1.5 flex items-center gap-1.5 text-[0.6875rem] text-muted">
527 <Avatar name={card.agent} size={14} />
528 <span className="font-mono text-faint">#{card.number}</span>
529 <span className="grow" />
530 {card.needsYou ? (
Merge branch 'worktree-agent-ae1299e92e4462012'531 <Hint label="Stopped: a person decides what happens next">
532 <span className="inline-flex items-center gap-1 text-warn">
533 <Hand size={11} /> you
534 <span className="sr-only">: stopped, a person decides what happens next</span>
535 </span>
536 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API537 ) : stage === "landed" ? (
538 <TimeAgo at={card.at} />
539 ) : card.runStarted ? (
Merge branch 'worktree-agent-ad7c6d88d93adc817'540 <span className="inline-flex items-center gap-1 text-accent">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API541 <Loader2 size={11} className="animate-spin" />
542 <Elapsed from={card.runStarted} />
543 </span>
544 ) : (
545 <CheckBadge status={card.checkStatus} />
546 )}
547 </span>
548 {card.step && (
Merge branch 'worktree-agent-ae1299e92e4462012'549 <Hint label={card.step}>
550 <span className="mt-1.5 block truncate font-mono text-[0.6875rem] text-fg/70">
551 {card.runKind}: {card.step}
552 </span>
553 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API554 )}
555 </Link>
556 </li>
557 );
558}
559
560/** Open pull requests moving through Working → Checking → Reviewing → Queue → Landed. */
561function Pipeline({ columns, base }: { columns: Loaded["columns"]; base: string }) {
562 return (
563 <div className="relative -mx-4 overflow-x-auto px-4 pb-1 sm:mx-0 sm:px-0">
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look564 {/* Each stage as tall as what is in it: an empty one stays short
565 instead of stretching to the busiest. */}
566 <ol className="grid min-w-176 grid-cols-5 items-start gap-2">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API567 {PIPELINE.map(({ stage, label }, index) => {
568 const cards = columns[stage];
569 return (
570 <li key={stage} className="flex min-w-0 flex-col rounded-xl border border-line bg-surface p-2">
571 <p className="flex items-center gap-2 px-1 pt-0.5 pb-2 text-xs font-medium text-muted">
572 <span className={`size-1.5 rounded-full ${STAGE_TONE[stage]} ${cards.length && stage !== "landed" ? "animate-pulse" : ""}`} />
573 {label}
574 <span className="tabular-nums text-faint">{cards.length}</span>
575 {index < PIPELINE.length - 1 && <ArrowRight size={11} className="ml-auto text-line-strong" />}
576 </p>
577 {cards.length === 0 ? (
578 <p className="rounded-lg border border-dashed border-line/70 px-2 py-3 text-center text-[0.6875rem] text-faint">Empty</p>
579 ) : (
580 <ul className="space-y-1.5">
581 {cards.slice(0, 6).map((card) => (
582 <PipelineCard key={card.number} card={card} base={base} stage={stage} />
583 ))}
584 {cards.length > 6 && (
585 <li className="px-1 text-[0.6875rem] text-muted">
586 <Link to={`${base}/pulls`} className="hover:text-fg">
587 {cards.length - 6} more
588 </Link>
589 </li>
590 )}
591 </ul>
592 )}
593 </li>
594 );
595 })}
596 </ol>
597 </div>
598 );
599}
600
A project is an app or a library: libraries show their package and how to ship a release, not production601/**
602 * Where an app's overview shows production, a library's shows what it
603 * publishes: each package with its latest version and how to install it,
604 * or, before there is one, how to publish a first version.
605 */
606function LibraryHead({
607 library,
608 project,
609 base,
610 workspace,
611}: {
612 library: { packages: PackageSummary[]; total: number; loaded: boolean };
613 project: Project | null;
614 base: string;
615 workspace: string;
616}) {
617 const guide = publishGuide(project?.ecosystem ?? null);
618 const name = project?.name ?? base.split("/").pop();
619 return (
620 <div className="p-5 sm:p-6">
621 <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase">
622 <PackageGlyph size={13} className="text-accent" />
623 Packages
624 </p>
625 {library.packages.length > 0 ? (
626 <>
627 <ul className="mt-3 space-y-4">
628 {library.packages.map((pkg) => {
629 const commands = installCommands(pkg, pkg.latest, "you");
630 return (
631 <li key={pkg.id} className="flex flex-col gap-2.5 lg:flex-row lg:items-start lg:gap-5">
632 <Link to={packagePath(pkg)} className="group flex min-w-0 items-center gap-3 lg:w-72 lg:shrink-0">
633 <PackageIcon ecosystem={pkg.ecosystem} size={32} />
634 <span className="min-w-0">
635 <span className="block truncate font-mono text-[0.9375rem] font-medium group-hover:text-accent">{packageName(pkg)}</span>
636 <span className="block text-xs text-muted">
637 {ECOSYSTEM_LABEL[pkg.ecosystem]} · {pkg.latest ?? "no versions yet"}
638 {pkg.versions > 1 && ` · ${pkg.versions} versions`}
639 </span>
640 </span>
641 </Link>
642 <div className="min-w-0 grow space-y-1.5">
643 {/* The workspace's registry first, or the tool installs a package of the same name from elsewhere. */}
644 {commands.registry && <CopyLine prompt text={commands.registry} />}
645 <CopyLine prompt text={commands.install} />
646 </div>
647 </li>
648 );
649 })}
650 </ul>
651 {library.total > library.packages.length && (
652 <Link to={`/${workspace}/-/packages`} className="mt-3 inline-block text-xs text-muted hover:text-fg">
653 All {library.total} packages
654 </Link>
655 )}
656 </>
657 ) : (
658 <>
659 <p className="mt-2 text-lg font-medium">{library.loaded ? "Not published yet" : "Packages could not be loaded just now"}</p>
660 {guide ? (
661 <>
662 <p className="mt-1 max-w-lg text-sm text-muted">
663 Publish a first version of {name} to the workspace's {guide.label} registry, for others to install
664 {guide.start ? ":" : "."}
665 </p>
666 {guide.start && (
667 <div className="mt-3 max-w-lg">
668 <CopyLine prompt text={guide.start} />
669 </div>
670 )}
671 <a href={guide.guide} className="mt-3 inline-flex items-center gap-1 text-sm text-accent hover:underline">
672 Publishing to {guide.label} <ArrowUpRight size={13} />
673 </a>
674 </>
675 ) : (
676 <>
677 <p className="mt-1 max-w-lg text-sm text-muted">Publish a first version of {name} to one of the workspace's registries:</p>
678 <p className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-sm">
679 {PUBLISH_GUIDES.map((one) => (
680 <a key={one.label} href={one.guide} className="inline-flex items-center gap-1 text-accent hover:underline">
681 {one.label} <ArrowUpRight size={13} />
682 </a>
683 ))}
684 </p>
685 </>
686 )}
687 </>
688 )}
689 </div>
690 );
691}
692
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API693function Stat({ label, value, to }: { label: string; value: ReactNode; to?: string }) {
694 const body = (
695 <>
696 <span className="block text-[0.6875rem] text-faint">{label}</span>
697 <span className="mt-0.5 block truncate text-[0.8125rem] font-medium">{value}</span>
698 </>
699 );
700 return to ? (
701 <Link to={to} className="min-w-0 px-5 py-3 transition-colors hover:bg-raised/50">
702 {body}
703 </Link>
704 ) : (
705 <div className="min-w-0 px-5 py-3">{body}</div>
706 );
707}
708
Projects: what a workspace builds and runs, first on every page709export default function ProjectOverview({ loaderData, actionData, params }: Route.ComponentProps) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily710 return (
711 <Suspense fallback={<OverviewSkeleton />}>
712 <Await resolve={loaderData.overview} errorElement={<OverviewFailed />}>
Merge branch 'worktree-agent-aca4790ea223aa235'713 {(overview) => (
714 // Faded in over its outline, not switched for it.
715 <div className="animate-fade-in">
716 <Overview loaderData={overview} actionData={actionData} params={params} />
717 </div>
718 )}
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily719 </Await>
720 </Suspense>
721 );
722}
723
724/** The overview's sections as they will sit, while they stream in. */
725function OverviewSkeleton() {
726 return (
Merge branch 'worktree-agent-aca4790ea223aa235'727 <Loading className="space-y-8" aria-label="Loading the overview">
728 {/* Production: its screenshot beside where it is live, then the facts under them. */}
729 <section aria-hidden="true" className="overflow-hidden rounded-2xl border border-line bg-surface">
730 <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6">
731 <Skeleton className="aspect-[16/10] w-full shrink-0 rounded-lg sm:w-72" />
732 <div className="min-w-0 grow">
733 <Skeleton className="h-3 w-24" />
734 <Skeleton className="mt-3 h-5 w-72 max-w-full" />
735 <Skeleton className="mt-3 h-3 w-48 max-w-full" />
736 </div>
737 </div>
738 <div className="grid grid-cols-2 gap-px border-t border-line bg-line sm:grid-cols-4">
739 {Array.from({ length: 4 }, (_, index) => (
740 <div key={index} className="bg-surface px-5 py-4">
741 <Skeleton className="h-3 w-16" />
742 <Skeleton className="mt-2.5 h-4 w-28" />
743 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily744 ))}
745 </div>
746 </section>
Merge branch 'worktree-agent-aca4790ea223aa235'747 {/* Right now: the work, column by column. */}
748 <div aria-hidden="true" className="grid grid-cols-2 gap-2 sm:grid-cols-5">
749 {Array.from({ length: 5 }, (_, index) => (
750 <div key={index} className="rounded-xl border border-line bg-surface p-3">
751 <Skeleton className="h-3 w-20" />
752 <Skeleton className="mt-3 h-14 rounded-lg" />
753 </div>
754 ))}
755 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily756 <div className="grid gap-6 lg:grid-cols-2">
757 <section className="rounded-2xl border border-line bg-surface">
758 <SkeletonRows rows={4} />
759 </section>
760 <section className="rounded-2xl border border-line bg-surface">
761 <SkeletonRows rows={4} />
762 </section>
763 </div>
Merge branch 'worktree-agent-aca4790ea223aa235'764 </Loading>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily765 );
766}
767
768function OverviewFailed() {
769 return (
770 <p className="rounded-2xl border border-line bg-surface p-6 text-sm text-muted">
771 The overview could not be loaded just now. Reload the page to try again.
772 </p>
773 );
774}
775
776function Overview({
777 loaderData,
778 actionData,
779 params,
780}: {
781 loaderData: Loaded;
782 actionData: Route.ComponentProps["actionData"];
783 params: Route.ComponentProps["params"];
784}) {
Projects no longer depend on each other: the dependsOn relation, its settings page (old links redirect), the overview card, a pull request's Affects panel and preview stacks, reference variables and agents' notes on what a project uses are gone; the table is dropped in a later deploy785 const { member, project, settings, builds, live, commit, open, agentsLive, columns, needs, landed, groups, health, knows, checklist, branches, library, checks } =
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look786 loaderData;
Projects: what a workspace builds and runs, first on every page787 const base = `/${params.owner}/${params.repo}`;
Merge branch 'mirroring' into artifacts-mode788 // What it is to its remotes, from the repository layout.
789 const { repo: mirrorRepo, briefs: mirrorBriefs, admin: mirrorAdmin } = useRepoMirror();
Projects: what a workspace builds and runs, first on every page790 const production = live.find((app) => app.kind === "production") ?? null;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains791 // The project's own domain, once it is active, is where production is visited.
792 const productionUrl = production ? (settings?.primaryDomain ? `https://${settings.primaryDomain}` : production.url) : null;
Projects: what a workspace builds and runs, first on every page793 const previews = live.filter((app) => app.kind === "preview");
794 const latestProduction = builds.find((build) => build.kind === "production") ?? null;
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97795 const plan = project ? checklistPlan(project).plan : "production";
796 const canChange = member && loaderData.can.manage_settings;
797 // Its homepage and docs beside what it is, unless the card below already links there.
798 const headUrl =
799 plan === "production"
800 ? (productionUrl ?? settings?.productionUrl)
801 : plan === "elsewhere"
802 ? (loaderData.deployment?.url ?? project?.productionUrl)
803 : plan === "docs"
804 ? (project?.links.docs ?? project?.productionUrl ?? project?.links.homepage)
805 : plan === "other"
806 ? (project?.links.homepage ?? project?.links.docs)
807 : null;
808 const stripLinks = project ? linksToShow({ ...project.links, custom: [] }, [headUrl]) : [];
Merge branch 'worktree-agent-aaf03bdceac799c89'809 const addresses = useAddresses();
Projects: what a workspace builds and runs, first on every page810 const source = project?.source.kind === "hosted" ? project.source : null;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API811 const moving = agentsLive.length > 0 || columns.working.length + columns.checking.length > 0;
812 useLiveRefresh(moving);
813 const ages = health.ages;
814 const oldest = ages ? Math.max(1, ...ages.map((a) => a.count)) : 1;
Projects: what a workspace builds and runs, first on every page815
816 return (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API817 <div className="space-y-8">
Merge branch 'mirroring' into artifacts-mode818 {loaderData.github?.mode === "import" ? (
819 <GithubLinkStrip link={loaderData.github} />
820 ) : (
821 <MirrorOverviewNote
822 base={base}
823 mirror={mirrorRepo?.mirror}
824 briefs={mirrorBriefs}
825 admin={mirrorAdmin}
826 syncedAt={loaderData.github?.syncedAt}
827 lastError={loaderData.github?.lastError}
828 />
829 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API830 {/* What the project is, running, and where its code is. */}
831 <section className="overflow-hidden rounded-2xl border border-line bg-surface">
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97832 {project && (
833 <div className="flex flex-wrap items-center gap-x-4 gap-y-2 border-b border-line px-5 py-2.5 text-xs sm:px-6">
834 <KindMenu project={project} label={kindLabel(project)} canChange={canChange} />
835 {stripLinks.map((link) => (
Merge branch 'site-fixes' into fast-push836 <DeployLink key={link.key} href={link.url} className="inline-flex min-w-0 items-center gap-1.5 text-muted hover:text-fg">
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97837 {link.type === "docs" ? <BookOpen size={12} className="shrink-0 text-faint" /> : <Globe size={12} className="shrink-0 text-faint" />}
838 <span className="truncate">{link.type === "docs" ? `Docs · ${bare(link.url)}` : link.label}</span>
Merge branch 'site-fixes' into fast-push839 </DeployLink>
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97840 ))}
841 </div>
842 )}
843 {plan === "elsewhere" && project ? (
844 <ElsewhereHead
845 project={project}
846 base={base}
847 canChange={canChange}
848 canDeploy={member && loaderData.can.manage_integrations}
849 deployment={loaderData.deployment}
850 commit={commit}
Merge checks: statuses and check runs on every commit851 checks={commit ? <CommitChecksBadge checks={checks} sha={commit.hash} className="size-5" /> : undefined}
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97852 />
853 ) : plan === "unknown" && project ? (
854 <WhereItRuns project={project} base={base} canChange={canChange} canDeploy={member && loaderData.can.manage_integrations} />
855 ) : plan === "docs" && project ? (
856 <DocsHead project={project} base={base} canChange={canChange} canDeploy={member && loaderData.can.manage_integrations} />
857 ) : plan === "other" && project ? (
858 <OtherHead project={project} base={base} canChange={canChange} />
859 ) : library ? (
860 <LibraryHead library={library} project={project} base={base} workspace={params.owner} />
A project is an app or a library: libraries show their package and how to ship a release, not production861 ) : (
862 <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6">
863 {member && (production || settings?.enabled) && (
864 <ProductionShot
865 src={production ? `${base}/production.jpg?v=${shotVersion(production)}` : null}
866 href={production ? (productionUrl ?? production.url) : null}
867 label={host(production ? (productionUrl ?? production.url) : (settings?.productionUrl ?? ""))}
868 className="w-full shrink-0 sm:w-60 lg:w-72"
869 />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API870 )}
A project is an app or a library: libraries show their package and how to ship a release, not production871 <div className="flex min-w-0 grow flex-wrap items-start justify-between gap-4">
872 <div className="min-w-0">
873 <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase">
874 <Rocket size={13} className="text-accent" />
875 Production
876 </p>
877 {production ? (
878 <>
Merge branch 'site-fixes' into fast-push879 <DeployLink
A project is an app or a library: libraries show their package and how to ship a release, not production880 href={productionUrl ?? production.url}
881 className="mt-2 flex items-center gap-1.5 truncate font-mono text-lg font-medium hover:text-accent"
882 >
883 {host(productionUrl ?? production.url)}
884 <ArrowUpRight size={16} className="shrink-0 text-faint" />
Merge branch 'site-fixes' into fast-push885 </DeployLink>
A project is an app or a library: libraries show their package and how to ship a release, not production886 <p className="mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted">
887 {latestProduction && <StatusDot status={latestProduction.status} label={latestProduction.status === "ready" ? "Live" : undefined} />}
888 <span className="inline-flex items-center gap-1 font-mono">
889 <GitCommitHorizontal size={13} className="text-faint" />
890 {production.commit.slice(0, 7)}
891 </span>
892 <span>
893 deployed <TimeAgo at={production.deployedAt} />
894 </span>
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97895 {settings?.primaryDomain && <span className="text-faint">also at {host(production.url)}</span>} </p>
A project is an app or a library: libraries show their package and how to ship a release, not production896 </>
897 ) : settings?.enabled ? (
898 <>
899 <p className="mt-2 font-mono text-lg text-muted">{host(settings.productionUrl)}</p>
900 <p className="mt-1.5 text-xs text-muted">
901 {latestProduction ? (
902 <StatusDot status={latestProduction.status} label={latestProduction.status === "failed" ? "The last build failed" : undefined} />
903 ) : (
904 "Not deployed yet. Push to the default branch, or deploy it now."
905 )}
906 </p>
907 </>
908 ) : (
909 <>
910 <p className="mt-2 text-lg font-medium">Deployments are off</p>
911 <p className="mt-1 max-w-lg text-sm text-muted">
912 Nothing builds or runs until you turn them on. Then {project?.name ?? params.repo} goes up on g1t.page:
913 production from the default branch, and a live preview for every pull request. It runs only while
914 someone visits.
915 </p>
916 </>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API917 )}
Projects: what a workspace builds and runs, first on every page918 </div>
A project is an app or a library: libraries show their package and how to ship a release, not production919 {member && (
920 <div className="flex shrink-0 items-center gap-2">
921 {production && (
Merge branch 'site-fixes' into fast-push922 <ButtonLink to={productionUrl ?? production.url} variant="accent" reloadDocument target="_blank" rel="noopener noreferrer">
A project is an app or a library: libraries show their package and how to ship a release, not production923 Visit
924 <ArrowUpRight size={14} />
925 </ButtonLink>
926 )}
927 {settings?.enabled ? (
928 loaderData.can.run && <Form method="post">
Merge branch 'worktree-agent-ae1299e92e4462012'929 <Hint label="Build production again from the default branch">
930 <SubmitButton variant="quiet" name="intent" value="redeploy" pending="Redeploying…">
931 <RotateCw size={14} />
932 Redeploy
933 </SubmitButton>
934 </Hint>
A project is an app or a library: libraries show their package and how to ship a release, not production935 </Form>
936 ) : loaderData.can.manage_integrations && (
937 <ButtonLink to={`${base}/settings/deployments`} variant="accent">
938 <Rocket size={14} />
939 Turn on deployments
940 </ButtonLink>
941 )}
942 </div>
943 )}
944 </div>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look945 </div>
A project is an app or a library: libraries show their package and how to ship a release, not production946 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API947 {actionData && (
Merge branch 'worktree-agent-ad7c6d88d93adc817'948 <p className={`border-t border-line px-6 py-2.5 text-sm ${"error" in actionData ? "text-danger" : "text-success"}`}>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API949 {"error" in actionData ? actionData.error : actionData.notice}
950 </p>
951 )}
952 <div className="grid grid-cols-2 divide-line border-t border-line text-sm sm:grid-cols-4 sm:divide-x">
953 <Stat
954 label="Source"
955 to={`${base}/code`}
956 value={
957 source ? (
958 <span className="inline-flex items-center gap-1.5 font-mono">
959 {project?.private ? <Lock size={12} className="text-faint" /> : <Code2 size={12} className="text-faint" />}
960 {source.repo.name}
961 {source.rootDir ? `/${source.rootDir}` : ""}
962 </span>
963 ) : (
964 "—"
965 )
966 }
967 />
968 <Stat
969 label="Default branch"
970 to={`${base}/commits`}
971 value={
972 <span className="inline-flex items-center gap-1.5 font-mono">
973 <GitBranch size={12} className="text-faint" />
974 {source?.defaultBranch ?? "main"}
975 </span>
976 }
977 />
978 <Stat
979 label="Latest commit"
980 value={
981 commit ? (
Merge checks: statuses and check runs on every commit982 <span className="flex min-w-0 items-center gap-1">
983 <Hint label={commit.message.split("\n")[0]}>
984 <Link to={`${base}/commit/${commit.hash}`} className="min-w-0 truncate hover:text-accent">
985 <span className="font-mono">{commit.hash.slice(0, 7)}</span>{" "}
986 <span className="font-normal text-muted">
987 <TimeAgo at={commit.authoredAt} />
988 </span>
989 </Link>
990 </Hint>
991 {/* A button, so beside the link rather than in it. */}
992 <CommitChecksBadge checks={checks} sha={commit.hash} className="size-5" />
993 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API994 ) : (
995 "No commits yet"
996 )
997 }
998 />
999 <Stat
A project is an app or a library: libraries show their package and how to ship a release, not production1000 label={member && settings?.enabled && !library ? "Last deploy" : "Open"}
1001 to={member && settings?.enabled && !library ? `${base}/deployments` : `${base}/pulls`}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1002 value={
A project is an app or a library: libraries show their package and how to ship a release, not production1003 member && settings?.enabled && !library ? (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1004 latestProduction ? (
1005 <span className="inline-flex items-center gap-2">
1006 <StatusDot status={latestProduction.status} />
1007 <span className="font-normal text-muted">
1008 <TimeAgo at={latestProduction.createdAt} />
Projects: what a workspace builds and runs, first on every page1009 </span>
1010 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1011 ) : (
1012 "None yet"
1013 )
1014 ) : (
1015 `${open.pulls} pull requests · ${open.issues} issues`
1016 )
1017 }
1018 />
1019 </div>
1020 {commit && (
1021 <p className="truncate border-t border-line px-5 py-2.5 text-xs text-muted sm:px-6">
1022 <GitCommitHorizontal size={12} className="mr-1.5 inline text-faint" />
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)1023 {commit.message.split("\n")[0]}{" "}
1024 <span className="text-faint">
1025 · <CommitNames commit={commit} className="hover:text-fg" />
1026 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1027 </p>
1028 )}
1029 </section>
1030
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971031 {checklist && <ProductionChecklist base={base} items={checklist} title={loaderData.checklistTitle} />}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1032
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1033 <Panel
1034 title="Right now"
Merge branch 'worktree-agent-ad7c6d88d93adc817'1035 icon={<span className={`block size-2 rounded-full ${agentsLive.length > 0 ? "animate-pulse bg-accent" : "bg-line-strong"}`} />}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1036 count={agentsLive.length}
1037 all={{ to: `${base}/agents`, label: "Agents" }}
1038 >
1039 {agentsLive.length > 0 && (
1040 <ul className="mb-3 grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3">
1041 {agentsLive.map((run) => (
Merge branch 'worktree-agent-ad7c6d88d93adc817'1042 <li key={run.id} className="min-w-0 rounded-xl border border-accent/30 bg-surface px-3.5 py-3">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1043 <div className="flex items-center gap-2 text-sm">
1044 <Avatar name={run.agent} size={18} />
1045 <span className="font-medium">{run.agent}</span>
1046 <span className="text-xs text-muted">{RUN_KIND_LABEL[run.kind].toLowerCase()}</span>
1047 <span className="grow" />
1048 <span className="text-xs text-muted">
1049 <Elapsed from={run.startedAt ?? run.createdAt} />
Projects: what a workspace builds and runs, first on every page1050 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1051 </div>
1052 <Link
1053 to={run.number != null ? `${base}/pull/${run.number}` : `${base}/agents/runs/${run.id}`}
1054 className="mt-1 block truncate text-[0.8125rem] hover:text-accent"
1055 >
1056 {run.title ?? "A run"} {run.number != null && <span className="text-faint">#{run.number}</span>}
1057 </Link>
1058 {run.step && (
Merge branch 'worktree-agent-ae1299e92e4462012'1059 <Hint label={run.step}>
1060 <p className="mt-1 truncate font-mono text-[0.6875rem] text-fg/70">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1061 <span className="mr-1.5 inline-block size-1.5 animate-pulse rounded-full bg-accent align-middle" />
Merge branch 'worktree-agent-ae1299e92e4462012'1062 {run.step}
1063 </p>
1064 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1065 )}
1066 <p className="mt-1.5 flex items-center gap-3 text-[0.6875rem] text-faint">
1067 {formatCost(run.costUsd) && <span>{formatCost(run.costUsd)} so far</span>}
1068 <Link to={`${base}/agents/runs/${run.id}`} className="hover:text-fg">
1069 {run.stepCount} steps
1070 </Link>
1071 {run.number != null && (
1072 <Link to={`${base}/sessions/${run.number}`} className="hover:text-fg">
1073 Session
1074 </Link>
1075 )}
1076 </p>
1077 </li>
1078 ))}
1079 </ul>
1080 )}
1081 {!loaderData.pullsLoaded ? (
1082 <Unavailable what="Pull requests" />
1083 ) : Object.values(columns).every((c) => c.length === 0) ? (
1084 <Quiet
1085 action={
1086 member ? (
You choose how g1t looks: Auto follows your system, Light and Dark stay put, set from the account menu, the phone's You and help sheet, Settings → Account or the command palette; the page is drawn in your choice from the first byte, every colour has a light value that reads at 4.5:1 or better, code and diffs are highlighted for both, diagrams and the document editor follow along, the product tour and the g1t mark's avatar stay dark, and Dark is what you see until you choose; the workspaces guide says how.1087 <Link to={`${base}/issues/new`} className="inline-flex items-center gap-1.5 rounded-md bg-fg px-3 py-1.5 text-xs font-medium text-bg hover:bg-fg-hover">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1088 <Bot size={12} /> Open an issue for an agent
1089 </Link>
1090 ) : null
1091 }
1092 >
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1093 Nothing is moving. Open an issue and hand it to g1t, or push a branch and open a pull request.
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1094 </Quiet>
1095 ) : (
1096 <Pipeline columns={columns} base={base} />
Projects: what a workspace builds and runs, first on every page1097 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1098 </Panel>
Projects: what a workspace builds and runs, first on every page1099
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1100 <div className="grid gap-x-8 gap-y-8 lg:grid-cols-[minmax(0,1fr)_20rem]">
1101 <div className="min-w-0 space-y-8">
1102 {needs.length > 0 && (
1103 <Panel title="Needs you" icon={<Hand size={14} className="text-warn" />} count={needs.length}>
1104 <NeedsList needs={needs} limit={5} />
1105 </Panel>
1106 )}
1107
Fast pages, required checks on the branch, self-hosted runners, honest incidents1108 <Suspense
1109 fallback={
1110 <Panel title="Active branches" icon={<GitBranch size={14} />}>
1111 <div aria-busy="true">
1112 <SkeletonRows rows={3} rowClassName="h-12" />
1113 </div>
1114 </Panel>
1115 }
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1116 >
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971117 <Await
1118 resolve={branches}
1119 errorElement={
1120 <Panel title="Active branches" icon={<GitBranch size={14} />}>
1121 <Unavailable what="Branches" />
1122 </Panel>
1123 }
1124 >
Fast pages, required checks on the branch, self-hosted runners, honest incidents1125 {(branches) => (
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971126 <Panel
1127 title="Active branches"
1128 icon={<GitBranch size={14} />}
1129 count={branches && branches !== "slow" ? branches.total || null : null}
1130 all={{ to: `${base}/branches`, label: "All branches" }}
1131 >
1132 {branches === "slow" ? (
1133 <Quiet>
1134 Its branches are taking a while to compare.{" "}
1135 <Link to={`${base}/branches`} className="text-accent hover:underline">
1136 See them on Branches
1137 </Link>
1138 .
1139 </Quiet>
1140 ) : !branches ? (
Fast pages, required checks on the branch, self-hosted runners, honest incidents1141 <Unavailable what="Branches" />
1142 ) : branches.shown.length === 0 ? (
1143 <Quiet>
1144 Only {branches.main} so far. Branches pushed here show with how far each has moved from {branches.main}, and
1145 its pull request and preview.
1146 </Quiet>
1147 ) : (
1148 <>
Merge checks: statuses and check runs on every commit1149 <ActiveBranches branches={branches.shown} base={base} main={branches.main} checks={checks} />
Fast pages, required checks on the branch, self-hosted runners, honest incidents1150 {branches.total > branches.shown.length && (
1151 <p className="mt-2 px-1 text-xs text-faint">
1152 The {branches.shown.length} most recently changed of {branches.total} branches.
1153 </p>
1154 )}
1155 </>
1156 )}
1157 </Panel>
1158 )}
1159 </Await>
1160 </Suspense>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1161
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1162 <Panel title="Recent changes" icon={<GitMerge size={14} />} all={{ to: `${base}/pulls?state=closed`, label: "All landed" }}>
1163 {landed.length === 0 ? (
1164 <Quiet>Nothing has landed yet. Merged pull requests show here with who made them and why.</Quiet>
Projects: what a workspace builds and runs, first on every page1165 ) : (
1166 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1167 {landed.map((pull) => {
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1168 const byAgent = pull.runtime === "hosted" || madeByG1t(pull);
Projects: what a workspace builds and runs, first on every page1169 return (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1170 <li key={pull.id} className="flex items-start gap-3 px-4 py-3">
1171 <GitMerge size={15} className="mt-0.5 shrink-0 text-merged" />
1172 <span className="min-w-0 grow">
1173 <Link to={`${base}/pull/${pull.number}`} prefetch="intent" className="block truncate text-sm font-medium hover:text-accent">
1174 {pull.title}
1175 </Link>
1176 <span className="mt-0.5 flex flex-wrap items-center gap-x-1.5 text-xs text-muted">
1177 <span className="font-mono text-faint">#{pull.number}</span>
1178 <span>·</span>
Overview: Recent changes show the avatar of whoever the line names, and every name opens its card1179 {/* The avatar is whoever the line names first: the
1180 agent that made it, or the person who wrote it
1181 with their own tools. */}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1182 <span className="inline-flex items-center gap-1">
Overview: Recent changes show the avatar of whoever the line names, and every name opens its card1183 {pull.requestedBy ? (
1184 <>
1185 <Avatar name={pull.author.username} size={13} />
1186 made by <PersonLink name={pull.author.username} className="hover:text-fg" /> for{" "}
1187 <PersonLink name={pull.requestedBy.username} className="hover:text-fg" />
1188 </>
1189 ) : byAgent ? (
1190 <>
1191 <Avatar name={pull.agent} size={13} />
1192 made by {pull.agent}
1193 </>
1194 ) : (
1195 <>
1196 <Avatar name={pull.author.username} size={13} />
1197 by <PersonLink name={pull.author.username} className="hover:text-fg" />
1198 </>
1199 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1200 </span>
1201 {pull.issue != null && (
1202 <>
1203 <span>· for</span>
1204 <Link to={`${base}/issues/${pull.issue}`} className="hover:text-fg">
1205 #{pull.issue}
1206 </Link>
1207 </>
1208 )}
Overview: Recent changes show the avatar of whoever the line names, and every name opens its card1209 {pull.mergedBy && (
1210 <span>
1211 · landed by <PersonLink name={pull.mergedBy} className="hover:text-fg" />
1212 </span>
1213 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1214 <span>
1215 · <TimeAgo at={pull.mergedAt ?? pull.updatedAt} />
Projects: what a workspace builds and runs, first on every page1216 </span>
1217 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1218 </span>
1219 <span className="hidden shrink-0 text-xs text-faint sm:block">
1220 <ChangeSize files={pull.files} />
1221 </span>
1222 {byAgent && (
1223 <Link to={`${base}/sessions/${pull.number}`} className="shrink-0 text-xs text-muted hover:text-fg">
1224 Session
1225 </Link>
1226 )}
Projects: what a workspace builds and runs, first on every page1227 </li>
1228 );
1229 })}
1230 </ul>
1231 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1232 </Panel>
Projects: what a workspace builds and runs, first on every page1233
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971234 {loaderData.commits.length > 0 && (
1235 <Panel
1236 title={`Latest on ${source?.defaultBranch ?? "main"}`}
1237 icon={<GitCommitHorizontal size={14} />}
1238 all={{ to: `${base}/commits`, label: "All commits" }}
1239 >
1240 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
1241 {loaderData.commits.map((one) => (
1242 <li key={one.hash} className="flex items-center gap-3 px-4 py-2.5 text-sm">
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)1243 <CommitAvatars commit={one} size={18} max={2} />
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971244 <span className="min-w-0 grow">
1245 <Link to={`${base}/commit/${one.hash}`} className="block truncate hover:text-accent">
1246 {one.message}
1247 </Link>
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)1248 <span className="text-xs text-muted">
1249 <CommitNames commit={one} className="hover:text-fg" />
1250 </span>
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971251 </span>
1252 <Link to={`${base}/commit/${one.hash}`} className="hidden shrink-0 font-mono text-xs text-faint hover:text-fg sm:block">
1253 {one.hash.slice(0, 7)}
1254 </Link>
1255 <span className="w-16 shrink-0 text-right text-xs text-faint">
1256 <TimeAgo at={one.at} />
1257 </span>
1258 </li>
1259 ))}
1260 </ul>
1261 </Panel>
1262 )}
1263
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1264 <Panel title="Activity" icon={<ActivityIcon size={14} />}>
1265 {loaderData.eventsLoaded ? (
1266 <ActivityFeed groups={groups} showRepo={false} limit={8} empty="Nothing has happened here yet." />
1267 ) : (
1268 <Unavailable what="Activity" />
1269 )}
1270 </Panel>
1271
1272 {previews.length > 0 && (
1273 <Panel title="Previews" icon={<GitBranch size={14} />} count={previews.length} all={{ to: `${base}/deployments`, label: "Deployments" }}>
1274 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
1275 {previews.map((app) => (
1276 <li key={app.url} className="flex items-center gap-3 px-4 py-2.5 text-sm">
1277 <span className="min-w-0 grow">
Merge branch 'site-fixes' into fast-push1278 <DeployLink href={app.url} className="block truncate font-mono text-[0.8125rem] hover:text-accent">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1279 {host(app.url)}
Merge branch 'site-fixes' into fast-push1280 </DeployLink>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1281 <span className="text-xs text-muted">
1282 {app.branch}
1283 {app.number != null && (
1284 <>
1285 {" · "}
1286 <Link to={`${base}/pull/${app.number}`} className="hover:underline">
1287 #{app.number}
1288 </Link>
1289 </>
1290 )}
1291 </span>
Projects: what a workspace builds and runs, first on every page1292 </span>
1293 <span className="shrink-0 text-xs text-faint">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1294 <TimeAgo at={app.deployedAt} />
Projects: what a workspace builds and runs, first on every page1295 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1296 </li>
1297 ))}
1298 </ul>
1299 </Panel>
1300 )}
1301 </div>
Projects: what a workspace builds and runs, first on every page1302
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1303 <aside className="space-y-4 text-sm">
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971304 {/* Deployments panel (deployments-panel.tsx): each environment's latest deployment. */}
1305 <DeploymentsPanel base={base} summary={loaderData.environments} className="rounded-xl border border-line bg-surface p-5" />
1306 {project && (
1307 <section className="rounded-xl border border-line bg-surface p-5">
1308 <div className="flex items-center justify-between">
1309 <h2 className="text-sm font-semibold">About</h2>
1310 {canChange && <AboutEditor project={project} />}
1311 </div>
1312 <p className={`mt-2.5 text-sm ${project.description ? "text-fg-soft" : "text-faint"}`}>
1313 {project.description ?? "No description."}
1314 </p>
1315 <LinkList links={project.links} className="mt-3" />
1316 <ul className="mt-4 space-y-2 text-xs text-muted">
1317 <li className="flex items-center gap-2">
1318 {project.kind === "library" || project.kind === "tool" ? (
1319 <PackageGlyph size={13} className="shrink-0 text-faint" />
1320 ) : project.kind === "docs" ? (
1321 <BookOpen size={13} className="shrink-0 text-faint" />
1322 ) : (
1323 <Rocket size={13} className="shrink-0 text-faint" />
1324 )}
1325 <Hint label={project.kindReason.detail}>
1326 <span>{kindLabel(project)}</span>
1327 </Hint>
1328 </li>
1329 {loaderData.release && (
1330 <li className="flex items-center gap-2">
1331 <Tag size={13} className="shrink-0 text-faint" />
1332 <Link to={`${base}/tags`} className="hover:text-fg">
1333 <span className="font-mono text-fg-soft">{loaderData.release.name}</span>
1334 {loaderData.release.at && (
1335 <span className="text-faint">
1336 {" · "}
1337 <TimeAgo at={loaderData.release.at} />
1338 </span>
1339 )}
1340 </Link>
1341 </li>
1342 )}
1343 {loaderData.languages && loaderData.languages.length > 0 && (
1344 <li className="flex items-center gap-2">
1345 <Code2 size={13} className="shrink-0 text-faint" />
1346 <span>
1347 {loaderData.languages
1348 .slice(0, 3)
1349 .map((language) => `${language.name} ${Math.round(language.share * 100)}%`)
1350 .join(" · ")}
1351 </span>
1352 </li>
1353 )}
1354 </ul>
1355 {loaderData.contributors && loaderData.contributors.length > 0 && (
1356 <div className="mt-4 flex flex-wrap gap-1">
1357 {loaderData.contributors.slice(0, 12).map((person) => (
1358 <Hint key={person.username} label={person.username}>
1359 <Link to={`/${person.username}`}>
1360 <Avatar name={person.username} size={22} />
1361 </Link>
1362 </Hint>
1363 ))}
1364 </div>
1365 )}
1366 </section>
1367 )}
1368
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1369 {member && (
1370 <section className="rounded-xl border border-line bg-surface p-5">
1371 <div className="flex items-center justify-between">
1372 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1373 <Brain size={14} className="text-accent" />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1374 What agents know here
1375 </h2>
1376 <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg">
1377 Memory
1378 </Link>
Projects: what a workspace builds and runs, first on every page1379 </div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1380 {!loaderData.memoriesLoaded ? (
1381 <p className="mt-3 text-xs text-muted">Memory could not be loaded just now.</p>
1382 ) : knows.length === 0 ? (
1383 <p className="mt-3 text-xs leading-5 text-muted">
1384 Nothing yet. Agents note conventions, decisions and traps as they work, and every agent starting here reads them.
1385 </p>
1386 ) : (
1387 <ul className="mt-3 space-y-2.5">
1388 {knows.map((memory) => (
1389 <li key={memory.id} className="text-xs leading-5">
1390 <p className="line-clamp-3 text-fg-soft">
1391 {memory.pinned && <Pin size={11} className="mr-1 inline text-accent" />}
Usage, Billing settings and prepaid AI credit; fixes from the UX audit1392 <InlineMarkdown text={memory.text} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1393 </p>
1394 <p className="text-faint">
1395 {memory.kind} · {memory.createdBy} · <TimeAgo at={memory.createdAt} />
1396 </p>
1397 </li>
1398 ))}
1399 {loaderData.memoryCount > knows.length && (
1400 <li>
1401 <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg">
1402 {loaderData.memoryCount - knows.length} more
1403 </Link>
1404 </li>
1405 )}
1406 </ul>
1407 )}
1408 </section>
Projects: what a workspace builds and runs, first on every page1409 )}
1410
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1411 <section className="rounded-xl border border-line bg-surface p-5">
1412 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
1413 <HeartPulse size={14} className="text-faint" />
1414 Health
Project dependencies: addresses, preview stacks, Affects, and agents who know1415 </h2>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1416 <div className="mt-4 space-y-4 text-xs">
1417 <div>
1418 <div className="flex items-baseline justify-between">
1419 <span className="text-muted">Checks passing</span>
1420 <span className="font-medium tabular-nums">{percent(health.passRate)}</span>
1421 </div>
1422 <div className="mt-1.5">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1423 <Meter value={health.passRate} tone={health.passRate != null && health.passRate < 0.7 ? "bg-warn" : "bg-success"} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1424 </div>
1425 <p className="mt-1 text-faint">
1426 {health.checkRuns
1427 ? `${health.checkRuns} recent runs · ${percent(health.firstPass.rate)} pass on the first try`
1428 : "No checks have run recently."}
1429 </p>
1430 </div>
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971431 {member && (plan === "production" || builds.length > 0) && (
Project dependencies: addresses, preview stacks, Affects, and agents who know1432 <div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1433 <div className="flex items-baseline justify-between">
1434 <span className="text-muted">Deploys</span>
1435 <Link to={`${base}/deployments`} className="text-faint hover:text-fg">
1436 {builds.length ? `last ${Math.min(builds.length, 20)}` : "none yet"}
1437 </Link>
1438 </div>
1439 <div className="mt-1.5">
1440 <DeployStrip builds={builds} base={base} />
1441 </div>
Project dependencies: addresses, preview stacks, Affects, and agents who know1442 </div>
1443 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1444 <div>
1445 <div className="flex items-baseline justify-between">
1446 <span className="text-muted">Open issues by age</span>
1447 <Link to={`${base}/issues`} className="font-medium tabular-nums hover:text-accent">
1448 {open.issues}
1449 </Link>
1450 </div>
1451 {ages ? (
1452 <ul className="mt-2 space-y-1.5">
1453 {ages.map((bucket) => (
1454 <li key={bucket.label} className="grid grid-cols-[6.5rem_1fr_1.5rem] items-center gap-2">
1455 <span className="text-faint">{bucket.label}</span>
1456 <span className="block h-1.5 overflow-hidden rounded-full bg-line">
1457 <span
1458 className={`block h-full rounded-full ${bucket.label === "Older" ? "bg-warn" : "bg-fg-soft/60"}`}
1459 style={{ width: `${(bucket.count / oldest) * 100}%` }}
1460 />
1461 </span>
1462 <span className="text-right tabular-nums text-muted">{bucket.count}</span>
Project dependencies: addresses, preview stacks, Affects, and agents who know1463 </li>
1464 ))}
1465 </ul>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1466 ) : (
1467 <p className="mt-1 text-faint">Issues could not be loaded just now.</p>
1468 )}
1469 </div>
Project dependencies: addresses, preview stacks, Affects, and agents who know1470 </div>
Projects: what a workspace builds and runs, first on every page1471 </section>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1472
1473 {source && (
1474 <section className="rounded-xl border border-line bg-surface p-5">
1475 <h2 className="text-sm font-semibold">Clone</h2>
1476 <div className="mt-3">
Merge demo dry run fixes: settled checks, other attempts, queue links, clone box, activity feed, DEMO.md1477 <CopyLine breakAtSlashes text={`git clone ${cloneUrl(addresses, `${source.repo.namespace}/${source.repo.name}`)}`} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1478 </div>
1479 </section>
1480 )}
1481 </aside>
1482 </div>
Projects: what a workspace builds and runs, first on every page1483 </div>
1484 );
1485}

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