Skip to content

g1t/apps/web/app/routes/repo/overview.tsx

1,477 lines68,076 bytesCodeBlame

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 ArrowDownLeft,
4 ArrowRight,
5 ArrowUpRight,
Merge main: Deployments panel in the About, project homepage, both sides' operations6 BookOpen,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API7 Bot,
8 Brain,
Merge main: Deployments panel in the About, project homepage, both sides' operations9 Globe,
10 Tag,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API11 Code2,
12 GitBranch,
13 GitCommitHorizontal,
14 GitMerge,
15 Hand,
16 HeartPulse,
17 Loader2,
18 Lock,
19 Network,
A project is an app or a library: libraries show their package and how to ship a release, not production20 Package as PackageGlyph,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API21 Pin,
22 Rocket,
23 RotateCw,
24} from "lucide-react";
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";
Projects: what a workspace builds and runs, first on every page42import { 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";
Usage, Billing settings and prepaid AI credit; fixes from the UX audit53import { distinctFacts } from "../../lib/memory-facts";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look54import { githubApp } from "../../lib/github.server";
Merge branch 'worktree-agent-a985c01f43538af01'55import { Avatar, ButtonLink, CopyLine, SubmitButton, TimeAgo } from "../../components/ui";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API56import { ChangeSize } from "../../components/work";
57import {
58 type ActivityItem,
59 type Need,
60 type PipelineStage,
61 PIPELINE,
62 TIME,
63 ageBuckets,
64 eventItem,
65 firstPassRate,
66 groupActivity,
67 passRate,
68 pipelineStage,
69 queuedNumbers,
70 rankNeeds,
71 stuckMinutes,
72} from "../../lib/mission";
Merge main: Deployments panel in the About, project homepage, both sides' operations73import { 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 production74import { ECOSYSTEM_LABEL, installCommands } from "../../lib/packages";
Merge main: Deployments panel in the About, project homepage, both sides' operations75import {
76 PUBLISH_GUIDES,
77 bare,
78 hasRelease,
79 kindLabel,
80 linksToShow,
81 latestTag,
82 libraryPackages,
83 packageName,
84 packagePath,
85 projectChanges,
86 publishGuide,
87} from "../../lib/project-kind";
88import { AboutEditor, KindMenu, LinkList } from "../../components/project-about";
89import { DocsHead, ElsewhereHead, type ExternalDeployment, OtherHead, WhereItRuns } from "../../components/project-head";
A project is an app or a library: libraries show their package and how to ship a release, not production90import { actions, agents, deployments, events as eventLog, 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-agent91import { madeByG1t } from "../../lib/opened-by";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look92import { assertSameOrigin, getViewer, requireUser } from "../../lib/session.server";
Fast pages, required checks on the branch, self-hosted runners, honest incidents93import { accessTo, countsFor, refusal, repoFor } from "../../lib/access.server";
Production thumbnails follow each deploy, not each commit, and old ones are cleared daily94import { shotVersion } from "./production-screenshot";
Merge main: Deployments panel in the About, project homepage, both sides' operations95import { DeploymentsPanel } from "../../components/deployments-panel";
96import { environmentUrl, productionEnvironment } from "../../lib/deployments";
Merge branch 'worktree-agent-aaf03bdceac799c89'97import { cloneUrl, useAddresses } from "../../lib/addresses";
Branches and Tags pages, each file's last commit, and the branch menu on files98import { readBranches } from "../../lib/branches.server";
Projects: what a workspace builds and runs, first on every page99
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API100const MAX_LANDED = 6;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look101/** Branches read for the Active branches list, and shown. */
102const BRANCHES_READ = 10;
103const BRANCHES_SHOWN = 5;
A project is an app or a library: libraries show their package and how to ship a release, not production104/** A library's packages shown on its overview; the rest are a link away. */
105const PACKAGES_SHOWN = 3;
Merge main: Deployments panel in the About, project homepage, both sides' operations106/** The default branch's latest commits listed. */
107const COMMITS_SHOWN = 5;
108/** How long Active branches may take before the section links to Branches instead. */
109const BRANCHES_WAIT_MS = 3_500;
Projects: what a workspace builds and runs, first on every page110
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily111/**
112 * The overview streams: the layout's header and tabs (one repository
113 * lookup, one project) go out at once with a skeleton below them, and
114 * everything here, seventeen calls across six services, follows in the
115 * same response as it settles. Crawlers wait for all of it
116 * (entry.server.tsx). Before, the first byte waited on the slowest of them.
117 */
118export function loader({ params, context }: Route.LoaderArgs) {
119 return { overview: overviewData({ params, context }) };
120}
121
122async function overviewData({ params, context }: Pick<Route.LoaderArgs, "params" | "context">) {
Projects: what a workspace builds and runs, first on every page123 const viewer = getViewer(context);
124 const path = { namespace: params.owner, name: params.repo };
125 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 API126 const now = Date.now();
127 // A section whose service fails shows its own empty state; the page stays up.
128 const soft = <T,>(promise: Promise<T> | null): Promise<T | null> =>
129 promise ? promise.catch((error) => (console.warn("overview:", error), null)) : Promise.resolve(null);
Fast pages, required checks on the branch, self-hosted runners, honest incidents130 // People with a role of their own here see its running parts: deployments,
131 // memory, setup; everyone who can read it sees the rest. Not awaited: only
132 // those sections wait on the repository lookup, which the layout makes in
133 // this request too.
134 const accessP = accessTo(context, params);
135 const memberP = accessP.then((access) => access.insider);
136 const forMembers = <T,>(start: () => Promise<T>): Promise<T | null> =>
137 memberP.then((member) => (member ? soft(start()) : null));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look138 const repoP = soft(repoFor(context, params));
A project is an app or a library: libraries show their package and how to ship a release, not production139 const projectP = soft(projects.get(params.owner, params.repo, viewer));
Merge main: Deployments panel in the About, project homepage, both sides' operations140 // A library or a tool shows its packages where an app shows production,
141 // and every project g1t does not deploy counts a workflow in its checklist.
142 // Deployments from anywhere (g1t.page, g1t Actions, the API), by environment, for anyone who can read it.
143 const environmentsP = projectP.then((found) =>
144 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 production145 );
Merge main: Deployments panel in the About, project homepage, both sides' operations146 const planP = projectP.then((project) => (project?.ok ? checklistPlan(project.value).plan : null));
147 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 production148 const packagesP = libraryRepoP.then((repo) => (repo ? soft(packages.list(params.owner, viewer, { repoId: repo.id })) : null));
Merge main: Deployments panel in the About, project homepage, both sides' operations149 const workflowsP = planP.then((plan) => (plan && plan !== "production" ? forMembers(() => actions.workflows(path, viewer)) : null));
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API150 const eventsP = repoP.then((repo) => (repo?.ok ? soft(eventLog.list({ repoId: repo.value.id, limit: 150 })) : null));
151 const minePullsP = repoP.then((repo) =>
152 repo?.ok && viewer
153 ? soft(work.listActivePulls(viewer)).then((list) => (list ?? []).filter((item) => item.pull.repoId === repo.value.id))
154 : null,
155 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look156 const openP = soft(work.listPulls(path, viewer, "open"));
157 // Where its code came from on GitHub, for members.
Fast pages, required checks on the branch, self-hosted runners, honest incidents158 const githubP = repoP.then((repo) => (repo?.ok ? forMembers(() => githubApp.link(repo.value.id)) : null));
159 const listP = forMembers(() => deployments.list(ref, viewer));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look160 // Active branches: the newest few besides the default, with how far each
161 // has moved. Branches with an open pull request are read first.
162 const branchesP = Promise.all([repoP, soft(repos.branches(path, viewer)), openP, listP]).then(
163 async ([repo, branchList, pulls, deploys]): Promise<{ main: string; total: number; shown: ActiveBranch[] } | null> => {
164 if (!repo?.ok || !branchList?.ok) return null;
165 const main = repo.value.defaultBranch;
Branches and Tags pages, each file's last commit, and the branch menu on files166 if (branchList.value.every((branch) => branch.name === main)) return { main, total: 0, shown: [] };
167 const read = await readBranches(
168 path,
169 viewer,
170 {
171 defaultBranch: main,
172 branches: branchList.value,
173 pulls: pulls?.ok ? pulls.value : [],
174 previews: deploys?.ok ? deploys.value.live.filter((app) => app.kind === "preview") : [],
175 },
176 BRANCHES_READ,
177 );
178 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 look179 },
180 );
Merge main: Deployments panel in the About, project homepage, both sides' operations181 // Active branches read several logs each: streamed, so the rest shows
182 // first. Bounded well inside the response's stream timeout
183 // (entry.server.tsx): a promise still pending when the stream ends never
184 // settles in the browser, and its skeleton would stay.
185 const branches = Promise.race([
186 branchesP.catch(() => null),
187 new Promise<"slow">((resolve) => setTimeout(() => resolve("slow"), BRANCHES_WAIT_MS)),
188 ]);
189 const [{ insider: member, can }, project, settings, list, open, closed, log, counts, deps, 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 incidents190 accessP,
A project is an app or a library: libraries show their package and how to ship a release, not production191 projectP,
Fast pages, required checks on the branch, self-hosted runners, honest incidents192 forMembers(() => deployments.settings(ref, viewer)),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look193 listP,
194 openP,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API195 soft(work.listPulls(path, viewer, "closed")),
Merge main: Deployments panel in the About, project homepage, both sides' operations196 soft(repos.log(path, viewer, null, COMMITS_SHOWN)),
Fast pages, required checks on the branch, self-hosted runners, honest incidents197 soft(countsFor(context, params)),
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API198 soft(projects.dependencies(params.owner, params.repo, viewer)),
199 soft(agents.listRuns(viewer, { repo: path, limit: 60 })),
200 soft(work.queue(path, viewer)),
201 soft(work.listIssues(path, viewer, { state: "open" })),
Fast pages, required checks on the branch, self-hosted runners, honest incidents202 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 API203 eventsP,
204 minePullsP,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look205 // For the checklist, which only members see.
Fast pages, required checks on the branch, self-hosted runners, honest incidents206 forMembers(() => deployments.domains(ref, viewer)),
207 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 production208 packagesP,
209 workflowsP,
Merge main: Deployments panel in the About, project homepage, both sides' operations210 // The latest release, for About.
211 soft(repos.tags(path, viewer)),
Projects: what a workspace builds and runs, first on every page212 ]);
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API213 const ok = <T,>(result: { ok: true; value: T } | { ok: false } | null): T | null => (result?.ok ? result.value : null);
214
215 const openPulls = ok(open) ?? [];
216 const runList = ok(runs) ?? [];
217 const live = runList.filter((run) => isActiveRun(run.status));
218 const builds = ok(list)?.deployments ?? [];
219 const landed = (ok(closed) ?? [])
220 .filter((pull) => pull.status === "merged")
221 .sort((a, b) => Date.parse(b.mergedAt ?? b.updatedAt) - Date.parse(a.mergedAt ?? a.updatedAt));
222 const queued = queuedNumbers(ok(queue)?.active ?? []);
223 const runOn = new Map<number, AgentRun>();
224 for (const run of live) if (run.number != null && !runOn.has(run.number)) runOn.set(run.number, run);
225 const lifecycleOf = new Map((mine ?? []).map((item) => [item.pull.number, item.lifecycle]));
226
227 // --- The pipeline -----------------------------------------------------------
228 type Card = {
229 number: number;
230 title: string;
231 agent: string;
232 status: Pull["status"];
233 checkStatus: Pull["checkStatus"];
234 step: string | null;
235 runKind: string | null;
236 runStarted: string | null;
237 needsYou: boolean;
238 at: string;
239 };
240 const columns: Record<PipelineStage, Card[]> = { working: [], checking: [], reviewing: [], queue: [], landed: [] };
241 for (const pull of openPulls.slice(0, 40)) {
242 const run = runOn.get(pull.number);
Fast pages, required checks on the branch, self-hosted runners, honest incidents243 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 API244 number: pull.number,
245 title: pull.title,
246 agent: pull.agent,
247 status: pull.status,
248 checkStatus: pull.checkStatus,
249 step: run?.step ?? null,
250 runKind: run ? RUN_KIND_LABEL[run.kind] : null,
251 runStarted: run ? (run.startedAt ?? run.createdAt) : null,
252 needsYou: lifecycleOf.get(pull.number)?.stage === "needs_you",
253 at: pull.updatedAt,
254 });
255 }
256 for (const pull of landed.filter((p) => now - Date.parse(p.mergedAt ?? p.updatedAt) < 3 * TIME.DAY).slice(0, 5)) {
257 columns.landed.push({
258 number: pull.number,
259 title: pull.title,
260 agent: pull.agent,
261 status: pull.status,
262 checkStatus: pull.checkStatus,
263 step: null,
264 runKind: null,
265 runStarted: null,
266 needsYou: false,
267 at: pull.mergedAt ?? pull.updatedAt,
268 });
269 }
270
271 // --- Needs you ---------------------------------------------------------------
272 const base = `/${params.owner}/${params.repo}`;
273 const needs: Need[] = [];
274 const latestProduction = builds.find((build) => build.kind === "production") ?? null;
275 if (latestProduction?.status === "failed") {
276 needs.push({
277 key: "deploy",
278 kind: "deploy",
279 title: "The production build failed",
280 detail: latestProduction.error ?? "Production still serves the build before it.",
281 to: `${base}/deployments/${latestProduction.id}`,
282 action: "See the build",
283 at: Date.parse(latestProduction.finishedAt ?? latestProduction.createdAt),
284 where: latestProduction.commit.slice(0, 7),
285 });
286 }
287 const me = viewer?.username.toLowerCase();
288 for (const pull of openPulls) {
289 const to = `${base}/pull/${pull.number}`;
290 const where = `#${pull.number}`;
291 const lifecycle = lifecycleOf.get(pull.number);
292 if (lifecycle?.stage === "needs_you") {
293 const conflict = /conflict/i.test(lifecycle.detail);
294 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 });
295 } else if (lifecycle?.stage === "ready") {
296 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 });
297 }
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights298 // Never your own, nor one g1t made for you; whoever it is for asked.
299 const owner = workOwner(pull).username;
300 if (me && pull.status === "open" && owner.toLowerCase() !== me && pull.reviewers.some((name) => name.toLowerCase() === me)) {
301 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 API302 }
303 if (member && pull.status === "open" && pull.checkStatus === "failed" && !runOn.has(pull.number) && !lifecycle) {
Fast pages, required checks on the branch, self-hosted runners, honest incidents304 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 API305 }
306 }
307 for (const run of live) {
308 const minutes = stuckMinutes(run, now);
309 if (minutes == null) continue;
310 needs.push({
311 key: `run:${run.id}`,
312 kind: "stuck",
313 title: run.title ?? `${run.agent}'s run`,
314 detail: `${run.agent} has reported nothing for ${minutes} min${run.step ? `. Last: ${run.step}` : ""}.`,
315 to: `${base}/agents/runs/${run.id}`,
316 action: "Look",
317 at: Date.parse(run.updatedAt),
318 where: run.number != null ? `#${run.number}` : null,
319 });
320 }
321
322 // --- Activity and health -------------------------------------------------------
323 const eventList: G1tEvent[] = recent ?? [];
324 const items = eventList
325 .map((event) => eventItem(event, path))
326 .filter((item): item is ActivityItem => item != null);
327 const checkEvents = eventList.flatMap((event) =>
328 event.type === "checks.completed" ? [{ repo: "", number: event.data.number, at: Date.parse(event.time), passed: event.data.status === "passed" }] : [],
329 );
330 const memoryList = ok(memories)?.project ?? [];
Usage, Billing settings and prepaid AI credit; fixes from the UX audit331 // Pinned first, then the newest; a fact remembered twice, once.
332 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 API333 const openIssues = ok(issues);
334
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look335 // --- Getting to production ----------------------------------------------------
336 const wentLive = (status: string) => status === "ready" || status === "replaced" || status === "down";
337 const liveApps = ok(list)?.live ?? [];
338 const commitNow = ok(log)?.[0] ?? null;
Merge main: Deployments panel in the About, project homepage, both sides' operations339 // Production deployed somewhere other than g1t.page (by g1t Actions, or
340 // reported through the API): the slot an app deployed elsewhere shows.
341 const environments = await environmentsP;
342 const environmentList = environments?.ok ? environments.value : null;
343 const reported = productionEnvironment(environmentList?.environments ?? []);
344 const deployment: ExternalDeployment | null =
345 reported?.latest && reported.latest.source !== "g1t_page"
346 ? {
347 environment: reported.name,
348 url: environmentUrl(reported),
349 state: reported.latest.state,
350 sha: reported.latest.sha,
351 created_at: reported.latest.updated_at,
352 }
353 : null;
354 // An app that has not said where it runs, but reports production from
355 // elsewhere, runs elsewhere.
356 const found = ok(project);
357 const projectValue = found && deployment && found.kind === "app" && found.runs == null ? { ...found, runs: "elsewhere" as const } : found;
358 const plan = projectValue ? checklistPlan(projectValue) : { plan: "production" as const, title: "Get to production" };
359 const isLibrary = plan.plan === "release";
A project is an app or a library: libraries show their package and how to ship a release, not production360 const published = libraryPackages(ok(packageList) ?? []);
Merge main: Deployments panel in the About, project homepage, both sides' operations361 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 production362 const firstSteps = {
363 base,
364 hasCode: commitNow != null || projectValue?.source.kind === "mirror",
365 instructions: commitNow == null ? false : root?.ok ? hasInstructions(root.value.entries.map((entry) => entry.name)) : null,
366 agentAssigned: agentWasAssigned({
367 runAgents: runList.map((run) => run.agent),
368 pullAgents: [...openPulls, ...(ok(closed) ?? [])].map((pull) => pull.agent),
369 issues: openIssues ?? [],
370 }),
371 };
Merge main: Deployments panel in the About, project homepage, both sides' operations372 const links = projectValue?.links;
A project is an app or a library: libraries show their package and how to ship a release, not production373 const checklist = !member
374 ? null
Merge main: Deployments panel in the About, project homepage, both sides' operations375 : plan.plan !== "production" && plan.plan !== "release"
376 ? startChecklist(plan.plan, {
377 ...firstSteps,
378 hasWorkflow,
379 productionUrl: projectValue?.productionUrl ?? null,
380 hasLinks: !!links && (links.homepage != null || links.docs != null || links.custom.length > 0),
381 hasDocsLink: !!links && (links.docs != null || links.homepage != null),
382 })
A project is an app or a library: libraries show their package and how to ship a release, not production383 : isLibrary
384 ? releaseChecklist({
385 ...firstSteps,
Merge main: Deployments panel in the About, project homepage, both sides' operations386 hasWorkflow,
A project is an app or a library: libraries show their package and how to ship a release, not production387 released: hasRelease(published),
388 releaseTo: published[0]
389 ? packagePath(published[0])
390 : (publishGuide(projectValue?.ecosystem ?? null)?.guide ?? "https://docs.g1t.sh/guides/packages/"),
391 })
392 : productionChecklist({
393 ...firstSteps,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look394 deploysEnabled: ok(settings)?.enabled ?? false,
395 productionDeployed:
396 liveApps.some((app) => app.kind === "production") ||
397 builds.some((build) => build.kind === "production" && wentLive(build.status)),
398 domains: ok(domains)?.domains.length ?? null,
399 previewOpened:
400 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 production401 });
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look402
403 const github = await githubP;
Projects: what a workspace builds and runs, first on every page404 return {
405 member,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look406 can,
407 github,
Merge main: Deployments panel in the About, project homepage, both sides' operations408 project: projectValue,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API409 settings: ok(settings),
410 builds: builds.slice(0, 30),
411 live: ok(list)?.live ?? [],
412 commit: ok(log)?.[0] ?? null,
Merge main: Deployments panel in the About, project homepage, both sides' operations413 commits: (ok(log) ?? []).map((one) => ({ hash: one.hash, message: one.message.split("\n")[0] ?? "", author: one.author.name, at: one.authoredAt })),
414 release: latestTag(ok(tagList) ?? []),
415 checklistTitle: plan.title,
416 // Production as reported from outside g1t, for an app deployed elsewhere.
417 deployment,
418 // Every environment's latest deployment, for the Deployments panel.
419 environments: environmentList,
420 // About's languages and contributors, filled in by the files' About work.
421 languages: null as { name: string; share: number }[] | null,
422 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 API423 open: ok(counts) ?? { issues: openIssues?.length ?? 0, pulls: openPulls.length },
424 dependencies: ok(deps),
425 agentsLive: live.slice(0, 6),
426 runsLoaded: runs?.ok ?? false,
427 pullsLoaded: open?.ok ?? false,
428 columns,
429 needs: rankNeeds(needs),
430 landed: landed.slice(0, MAX_LANDED),
431 groups: groupActivity(items).slice(0, 30),
432 eventsLoaded: recent != null,
433 health: {
434 passRate: passRate(checkEvents.map((c) => c.passed)),
435 firstPass: firstPassRate(checkEvents),
436 checkRuns: checkEvents.length,
437 ages: openIssues ? ageBuckets(openIssues.map((issue) => issue.createdAt), now) : null,
438 },
439 knows: knows as Memory[],
440 memoryCount: memoryList.length,
441 memoriesLoaded: memories?.ok ?? false,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look442 checklist,
A project is an app or a library: libraries show their package and how to ship a release, not production443 library: isLibrary
444 ? { packages: published.slice(0, PACKAGES_SHOWN), total: published.length, loaded: packageList?.ok ?? false }
445 : null,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look446 branches,
Projects: what a workspace builds and runs, first on every page447 };
448}
449
450export async function action({ request, params, context }: Route.ActionArgs) {
451 assertSameOrigin(request);
452 const user = requireUser(context, request);
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look453 const form = await request.formData().catch(() => null);
454 const intent = form?.get("intent");
Merge main: Deployments panel in the About, project homepage, both sides' operations455 // What it is, where it runs, its description and links: its settings.
456 if (form && (intent === "kind" || intent === "about")) {
457 const refused = await refusal(context, params, "manage_settings");
458 if (refused) return { error: refused };
459 const saved = await projects.update(user, params.owner, params.repo, projectChanges(form));
460 return saved.ok ? { notice: "Saved." } : { error: saved.error.message };
461 }
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look462 // Syncing from GitHub is pushing; stopping it is an integration; deploying is compute.
463 const refused = await refusal(context, params, intent === "github-sync" ? "push" : intent === "github-stop" ? "manage_integrations" : "run");
464 if (refused) return { error: refused };
465 if (intent === "github-sync" || intent === "github-stop") {
466 const repo = await repos.get({ namespace: params.owner, name: params.repo }, user);
467 if (!repo.ok) return { error: repo.error.message };
468 const done =
469 intent === "github-sync" ? await githubApp.sync(user, repo.value.id) : await githubApp.unlinkRepo(user, repo.value.id);
470 return done.ok
471 ? { notice: intent === "github-sync" ? "Synced with GitHub." : "It is no longer kept in step with GitHub." }
472 : { error: done.error.message };
473 }
Projects: what a workspace builds and runs, first on every page474 const started = await deployments.redeploy(user, { workspace: params.owner, slug: params.repo }, null);
475 return started.ok ? { notice: "Production is building." } : { error: started.error.message };
476}
477
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily478type Loaded = Awaited<ReturnType<typeof overviewData>>;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API479type Card = Loaded["columns"]["working"][number];
480
481const STAGE_TONE: Record<PipelineStage, string> = {
Merge branch 'worktree-agent-ad7c6d88d93adc817'482 working: "bg-accent",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API483 checking: "bg-info",
484 reviewing: "bg-warn",
Merge branch 'worktree-agent-ad7c6d88d93adc817'485 queue: "bg-success-dim",
486 landed: "bg-success",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API487};
488
489function PipelineCard({ card, base, stage }: { card: Card; base: string; stage: PipelineStage }) {
490 return (
491 <li>
492 <Link
493 to={`${base}/pull/${card.number}`}
494 prefetch="intent"
495 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'496 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 API497 }`}
498 >
499 <span className="line-clamp-2 text-[0.8125rem] leading-snug font-medium">{card.title}</span>
500 <span className="mt-1.5 flex items-center gap-1.5 text-[0.6875rem] text-muted">
501 <Avatar name={card.agent} size={14} />
502 <span className="font-mono text-faint">#{card.number}</span>
503 <span className="grow" />
504 {card.needsYou ? (
Merge branch 'worktree-agent-ae1299e92e4462012'505 <Hint label="Stopped: a person decides what happens next">
506 <span className="inline-flex items-center gap-1 text-warn">
507 <Hand size={11} /> you
508 <span className="sr-only">: stopped, a person decides what happens next</span>
509 </span>
510 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API511 ) : stage === "landed" ? (
512 <TimeAgo at={card.at} />
513 ) : card.runStarted ? (
Merge branch 'worktree-agent-ad7c6d88d93adc817'514 <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 API515 <Loader2 size={11} className="animate-spin" />
516 <Elapsed from={card.runStarted} />
517 </span>
518 ) : (
519 <CheckBadge status={card.checkStatus} />
520 )}
521 </span>
522 {card.step && (
Merge branch 'worktree-agent-ae1299e92e4462012'523 <Hint label={card.step}>
524 <span className="mt-1.5 block truncate font-mono text-[0.6875rem] text-fg/70">
525 {card.runKind}: {card.step}
526 </span>
527 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API528 )}
529 </Link>
530 </li>
531 );
532}
533
534/** Open pull requests moving through Working → Checking → Reviewing → Queue → Landed. */
535function Pipeline({ columns, base }: { columns: Loaded["columns"]; base: string }) {
536 return (
537 <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 look538 {/* Each stage as tall as what is in it: an empty one stays short
539 instead of stretching to the busiest. */}
540 <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 API541 {PIPELINE.map(({ stage, label }, index) => {
542 const cards = columns[stage];
543 return (
544 <li key={stage} className="flex min-w-0 flex-col rounded-xl border border-line bg-surface p-2">
545 <p className="flex items-center gap-2 px-1 pt-0.5 pb-2 text-xs font-medium text-muted">
546 <span className={`size-1.5 rounded-full ${STAGE_TONE[stage]} ${cards.length && stage !== "landed" ? "animate-pulse" : ""}`} />
547 {label}
548 <span className="tabular-nums text-faint">{cards.length}</span>
549 {index < PIPELINE.length - 1 && <ArrowRight size={11} className="ml-auto text-line-strong" />}
550 </p>
551 {cards.length === 0 ? (
552 <p className="rounded-lg border border-dashed border-line/70 px-2 py-3 text-center text-[0.6875rem] text-faint">Empty</p>
553 ) : (
554 <ul className="space-y-1.5">
555 {cards.slice(0, 6).map((card) => (
556 <PipelineCard key={card.number} card={card} base={base} stage={stage} />
557 ))}
558 {cards.length > 6 && (
559 <li className="px-1 text-[0.6875rem] text-muted">
560 <Link to={`${base}/pulls`} className="hover:text-fg">
561 {cards.length - 6} more
562 </Link>
563 </li>
564 )}
565 </ul>
566 )}
567 </li>
568 );
569 })}
570 </ol>
571 </div>
572 );
573}
574
A project is an app or a library: libraries show their package and how to ship a release, not production575/**
576 * Where an app's overview shows production, a library's shows what it
577 * publishes: each package with its latest version and how to install it,
578 * or, before there is one, how to publish a first version.
579 */
580function LibraryHead({
581 library,
582 project,
583 base,
584 workspace,
585}: {
586 library: { packages: PackageSummary[]; total: number; loaded: boolean };
587 project: Project | null;
588 base: string;
589 workspace: string;
590}) {
591 const guide = publishGuide(project?.ecosystem ?? null);
592 const name = project?.name ?? base.split("/").pop();
593 return (
594 <div className="p-5 sm:p-6">
595 <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase">
596 <PackageGlyph size={13} className="text-accent" />
597 Packages
598 </p>
599 {library.packages.length > 0 ? (
600 <>
601 <ul className="mt-3 space-y-4">
602 {library.packages.map((pkg) => {
603 const commands = installCommands(pkg, pkg.latest, "you");
604 return (
605 <li key={pkg.id} className="flex flex-col gap-2.5 lg:flex-row lg:items-start lg:gap-5">
606 <Link to={packagePath(pkg)} className="group flex min-w-0 items-center gap-3 lg:w-72 lg:shrink-0">
607 <PackageIcon ecosystem={pkg.ecosystem} size={32} />
608 <span className="min-w-0">
609 <span className="block truncate font-mono text-[0.9375rem] font-medium group-hover:text-accent">{packageName(pkg)}</span>
610 <span className="block text-xs text-muted">
611 {ECOSYSTEM_LABEL[pkg.ecosystem]} · {pkg.latest ?? "no versions yet"}
612 {pkg.versions > 1 && ` · ${pkg.versions} versions`}
613 </span>
614 </span>
615 </Link>
616 <div className="min-w-0 grow space-y-1.5">
617 {/* The workspace's registry first, or the tool installs a package of the same name from elsewhere. */}
618 {commands.registry && <CopyLine prompt text={commands.registry} />}
619 <CopyLine prompt text={commands.install} />
620 </div>
621 </li>
622 );
623 })}
624 </ul>
625 {library.total > library.packages.length && (
626 <Link to={`/${workspace}/-/packages`} className="mt-3 inline-block text-xs text-muted hover:text-fg">
627 All {library.total} packages
628 </Link>
629 )}
630 </>
631 ) : (
632 <>
633 <p className="mt-2 text-lg font-medium">{library.loaded ? "Not published yet" : "Packages could not be loaded just now"}</p>
634 {guide ? (
635 <>
636 <p className="mt-1 max-w-lg text-sm text-muted">
637 Publish a first version of {name} to the workspace's {guide.label} registry, for others to install
638 {guide.start ? ":" : "."}
639 </p>
640 {guide.start && (
641 <div className="mt-3 max-w-lg">
642 <CopyLine prompt text={guide.start} />
643 </div>
644 )}
645 <a href={guide.guide} className="mt-3 inline-flex items-center gap-1 text-sm text-accent hover:underline">
646 Publishing to {guide.label} <ArrowUpRight size={13} />
647 </a>
648 </>
649 ) : (
650 <>
651 <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>
652 <p className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-sm">
653 {PUBLISH_GUIDES.map((one) => (
654 <a key={one.label} href={one.guide} className="inline-flex items-center gap-1 text-accent hover:underline">
655 {one.label} <ArrowUpRight size={13} />
656 </a>
657 ))}
658 </p>
659 </>
660 )}
661 </>
662 )}
663 </div>
664 );
665}
666
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API667function Stat({ label, value, to }: { label: string; value: ReactNode; to?: string }) {
668 const body = (
669 <>
670 <span className="block text-[0.6875rem] text-faint">{label}</span>
671 <span className="mt-0.5 block truncate text-[0.8125rem] font-medium">{value}</span>
672 </>
673 );
674 return to ? (
675 <Link to={to} className="min-w-0 px-5 py-3 transition-colors hover:bg-raised/50">
676 {body}
677 </Link>
678 ) : (
679 <div className="min-w-0 px-5 py-3">{body}</div>
680 );
681}
682
Projects: what a workspace builds and runs, first on every page683export default function ProjectOverview({ loaderData, actionData, params }: Route.ComponentProps) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily684 return (
685 <Suspense fallback={<OverviewSkeleton />}>
686 <Await resolve={loaderData.overview} errorElement={<OverviewFailed />}>
Merge branch 'worktree-agent-aca4790ea223aa235'687 {(overview) => (
688 // Faded in over its outline, not switched for it.
689 <div className="animate-fade-in">
690 <Overview loaderData={overview} actionData={actionData} params={params} />
691 </div>
692 )}
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily693 </Await>
694 </Suspense>
695 );
696}
697
698/** The overview's sections as they will sit, while they stream in. */
699function OverviewSkeleton() {
700 return (
Merge branch 'worktree-agent-aca4790ea223aa235'701 <Loading className="space-y-8" aria-label="Loading the overview">
702 {/* Production: its screenshot beside where it is live, then the facts under them. */}
703 <section aria-hidden="true" className="overflow-hidden rounded-2xl border border-line bg-surface">
704 <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6">
705 <Skeleton className="aspect-[16/10] w-full shrink-0 rounded-lg sm:w-72" />
706 <div className="min-w-0 grow">
707 <Skeleton className="h-3 w-24" />
708 <Skeleton className="mt-3 h-5 w-72 max-w-full" />
709 <Skeleton className="mt-3 h-3 w-48 max-w-full" />
710 </div>
711 </div>
712 <div className="grid grid-cols-2 gap-px border-t border-line bg-line sm:grid-cols-4">
713 {Array.from({ length: 4 }, (_, index) => (
714 <div key={index} className="bg-surface px-5 py-4">
715 <Skeleton className="h-3 w-16" />
716 <Skeleton className="mt-2.5 h-4 w-28" />
717 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily718 ))}
719 </div>
720 </section>
Merge branch 'worktree-agent-aca4790ea223aa235'721 {/* Right now: the work, column by column. */}
722 <div aria-hidden="true" className="grid grid-cols-2 gap-2 sm:grid-cols-5">
723 {Array.from({ length: 5 }, (_, index) => (
724 <div key={index} className="rounded-xl border border-line bg-surface p-3">
725 <Skeleton className="h-3 w-20" />
726 <Skeleton className="mt-3 h-14 rounded-lg" />
727 </div>
728 ))}
729 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily730 <div className="grid gap-6 lg:grid-cols-2">
731 <section className="rounded-2xl border border-line bg-surface">
732 <SkeletonRows rows={4} />
733 </section>
734 <section className="rounded-2xl border border-line bg-surface">
735 <SkeletonRows rows={4} />
736 </section>
737 </div>
Merge branch 'worktree-agent-aca4790ea223aa235'738 </Loading>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily739 );
740}
741
742function OverviewFailed() {
743 return (
744 <p className="rounded-2xl border border-line bg-surface p-6 text-sm text-muted">
745 The overview could not be loaded just now. Reload the page to try again.
746 </p>
747 );
748}
749
750function Overview({
751 loaderData,
752 actionData,
753 params,
754}: {
755 loaderData: Loaded;
756 actionData: Route.ComponentProps["actionData"];
757 params: Route.ComponentProps["params"];
758}) {
A project is an app or a library: libraries show their package and how to ship a release, not production759 const { member, project, settings, builds, live, commit, open, dependencies, agentsLive, columns, needs, landed, groups, health, knows, checklist, branches, library } =
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look760 loaderData;
Projects: what a workspace builds and runs, first on every page761 const base = `/${params.owner}/${params.repo}`;
762 const production = live.find((app) => app.kind === "production") ?? null;
Agents and memory, checks and conflicts, profiles, slug renames, custom domains763 // The project's own domain, once it is active, is where production is visited.
764 const productionUrl = production ? (settings?.primaryDomain ? `https://${settings.primaryDomain}` : production.url) : null;
Projects: what a workspace builds and runs, first on every page765 const previews = live.filter((app) => app.kind === "preview");
766 const latestProduction = builds.find((build) => build.kind === "production") ?? null;
Merge main: Deployments panel in the About, project homepage, both sides' operations767 const plan = project ? checklistPlan(project).plan : "production";
768 const canChange = member && loaderData.can.manage_settings;
769 // Its homepage and docs beside what it is, unless the card below already links there.
770 const headUrl =
771 plan === "production"
772 ? (productionUrl ?? settings?.productionUrl)
773 : plan === "elsewhere"
774 ? (loaderData.deployment?.url ?? project?.productionUrl)
775 : plan === "docs"
776 ? (project?.links.docs ?? project?.productionUrl ?? project?.links.homepage)
777 : plan === "other"
778 ? (project?.links.homepage ?? project?.links.docs)
779 : null;
780 const stripLinks = project ? linksToShow({ ...project.links, custom: [] }, [headUrl]) : [];
Merge branch 'worktree-agent-aaf03bdceac799c89'781 const addresses = useAddresses();
Projects: what a workspace builds and runs, first on every page782 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 API783 const moving = agentsLive.length > 0 || columns.working.length + columns.checking.length > 0;
784 useLiveRefresh(moving);
785 const ages = health.ages;
786 const oldest = ages ? Math.max(1, ...ages.map((a) => a.count)) : 1;
Projects: what a workspace builds and runs, first on every page787
788 return (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API789 <div className="space-y-8">
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look790 {loaderData.github && <GithubLinkStrip link={loaderData.github} />}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API791 {/* What the project is, running, and where its code is. */}
792 <section className="overflow-hidden rounded-2xl border border-line bg-surface">
Merge main: Deployments panel in the About, project homepage, both sides' operations793 {project && (
794 <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">
795 <KindMenu project={project} label={kindLabel(project)} canChange={canChange} />
796 {stripLinks.map((link) => (
797 <a key={link.key} href={link.url} rel="noopener noreferrer" className="inline-flex min-w-0 items-center gap-1.5 text-muted hover:text-fg">
798 {link.type === "docs" ? <BookOpen size={12} className="shrink-0 text-faint" /> : <Globe size={12} className="shrink-0 text-faint" />}
799 <span className="truncate">{link.type === "docs" ? `Docs · ${bare(link.url)}` : link.label}</span>
800 </a>
801 ))}
802 </div>
803 )}
804 {plan === "elsewhere" && project ? (
805 <ElsewhereHead
806 project={project}
807 base={base}
808 canChange={canChange}
809 canDeploy={member && loaderData.can.manage_integrations}
810 deployment={loaderData.deployment}
811 commit={commit}
812 />
813 ) : plan === "unknown" && project ? (
814 <WhereItRuns project={project} base={base} canChange={canChange} canDeploy={member && loaderData.can.manage_integrations} />
815 ) : plan === "docs" && project ? (
816 <DocsHead project={project} base={base} canChange={canChange} canDeploy={member && loaderData.can.manage_integrations} />
817 ) : plan === "other" && project ? (
818 <OtherHead project={project} base={base} canChange={canChange} />
819 ) : library ? (
820 <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 production821 ) : (
822 <div className="flex flex-col gap-5 p-5 sm:flex-row sm:items-start sm:p-6">
823 {member && (production || settings?.enabled) && (
824 <ProductionShot
825 src={production ? `${base}/production.jpg?v=${shotVersion(production)}` : null}
826 href={production ? (productionUrl ?? production.url) : null}
827 label={host(production ? (productionUrl ?? production.url) : (settings?.productionUrl ?? ""))}
828 className="w-full shrink-0 sm:w-60 lg:w-72"
829 />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API830 )}
A project is an app or a library: libraries show their package and how to ship a release, not production831 <div className="flex min-w-0 grow flex-wrap items-start justify-between gap-4">
832 <div className="min-w-0">
833 <p className="flex items-center gap-2 text-xs font-medium tracking-wide text-muted uppercase">
834 <Rocket size={13} className="text-accent" />
835 Production
836 </p>
837 {production ? (
838 <>
839 <a
840 href={productionUrl ?? production.url}
841 className="mt-2 flex items-center gap-1.5 truncate font-mono text-lg font-medium hover:text-accent"
842 >
843 {host(productionUrl ?? production.url)}
844 <ArrowUpRight size={16} className="shrink-0 text-faint" />
845 </a>
846 <p className="mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted">
847 {latestProduction && <StatusDot status={latestProduction.status} label={latestProduction.status === "ready" ? "Live" : undefined} />}
848 <span className="inline-flex items-center gap-1 font-mono">
849 <GitCommitHorizontal size={13} className="text-faint" />
850 {production.commit.slice(0, 7)}
851 </span>
852 <span>
853 deployed <TimeAgo at={production.deployedAt} />
854 </span>
Merge main: Deployments panel in the About, project homepage, both sides' operations855 {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 production856 </>
857 ) : settings?.enabled ? (
858 <>
859 <p className="mt-2 font-mono text-lg text-muted">{host(settings.productionUrl)}</p>
860 <p className="mt-1.5 text-xs text-muted">
861 {latestProduction ? (
862 <StatusDot status={latestProduction.status} label={latestProduction.status === "failed" ? "The last build failed" : undefined} />
863 ) : (
864 "Not deployed yet. Push to the default branch, or deploy it now."
865 )}
866 </p>
867 </>
868 ) : (
869 <>
870 <p className="mt-2 text-lg font-medium">Deployments are off</p>
871 <p className="mt-1 max-w-lg text-sm text-muted">
872 Nothing builds or runs until you turn them on. Then {project?.name ?? params.repo} goes up on g1t.page:
873 production from the default branch, and a live preview for every pull request. It runs only while
874 someone visits.
875 </p>
876 </>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API877 )}
Projects: what a workspace builds and runs, first on every page878 </div>
A project is an app or a library: libraries show their package and how to ship a release, not production879 {member && (
880 <div className="flex shrink-0 items-center gap-2">
881 {production && (
882 <ButtonLink to={productionUrl ?? production.url} variant="accent" reloadDocument>
883 Visit
884 <ArrowUpRight size={14} />
885 </ButtonLink>
886 )}
887 {settings?.enabled ? (
888 loaderData.can.run && <Form method="post">
Merge branch 'worktree-agent-ae1299e92e4462012'889 <Hint label="Build production again from the default branch">
890 <SubmitButton variant="quiet" name="intent" value="redeploy" pending="Redeploying…">
891 <RotateCw size={14} />
892 Redeploy
893 </SubmitButton>
894 </Hint>
A project is an app or a library: libraries show their package and how to ship a release, not production895 </Form>
896 ) : loaderData.can.manage_integrations && (
897 <ButtonLink to={`${base}/settings/deployments`} variant="accent">
898 <Rocket size={14} />
899 Turn on deployments
900 </ButtonLink>
901 )}
902 </div>
903 )}
904 </div>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look905 </div>
A project is an app or a library: libraries show their package and how to ship a release, not production906 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API907 {actionData && (
Merge branch 'worktree-agent-ad7c6d88d93adc817'908 <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 API909 {"error" in actionData ? actionData.error : actionData.notice}
910 </p>
911 )}
912 <div className="grid grid-cols-2 divide-line border-t border-line text-sm sm:grid-cols-4 sm:divide-x">
913 <Stat
914 label="Source"
915 to={`${base}/code`}
916 value={
917 source ? (
918 <span className="inline-flex items-center gap-1.5 font-mono">
919 {project?.private ? <Lock size={12} className="text-faint" /> : <Code2 size={12} className="text-faint" />}
920 {source.repo.name}
921 {source.rootDir ? `/${source.rootDir}` : ""}
922 </span>
923 ) : (
924 "—"
925 )
926 }
927 />
928 <Stat
929 label="Default branch"
930 to={`${base}/commits`}
931 value={
932 <span className="inline-flex items-center gap-1.5 font-mono">
933 <GitBranch size={12} className="text-faint" />
934 {source?.defaultBranch ?? "main"}
935 </span>
936 }
937 />
938 <Stat
939 label="Latest commit"
940 to={commit ? `${base}/commit/${commit.hash}` : undefined}
941 value={
942 commit ? (
Merge branch 'worktree-agent-ae1299e92e4462012'943 <Hint label={commit.message.split("\n")[0]}>
944 <span>
945 <span className="font-mono">{commit.hash.slice(0, 7)}</span>{" "}
946 <span className="font-normal text-muted">
947 <TimeAgo at={commit.authoredAt} />
948 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API949 </span>
Merge branch 'worktree-agent-ae1299e92e4462012'950 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API951 ) : (
952 "No commits yet"
953 )
954 }
955 />
956 <Stat
A project is an app or a library: libraries show their package and how to ship a release, not production957 label={member && settings?.enabled && !library ? "Last deploy" : "Open"}
958 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 API959 value={
A project is an app or a library: libraries show their package and how to ship a release, not production960 member && settings?.enabled && !library ? (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API961 latestProduction ? (
962 <span className="inline-flex items-center gap-2">
963 <StatusDot status={latestProduction.status} />
964 <span className="font-normal text-muted">
965 <TimeAgo at={latestProduction.createdAt} />
Projects: what a workspace builds and runs, first on every page966 </span>
967 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API968 ) : (
969 "None yet"
970 )
971 ) : (
972 `${open.pulls} pull requests · ${open.issues} issues`
973 )
974 }
975 />
976 </div>
977 {commit && (
978 <p className="truncate border-t border-line px-5 py-2.5 text-xs text-muted sm:px-6">
979 <GitCommitHorizontal size={12} className="mr-1.5 inline text-faint" />
980 {commit.message.split("\n")[0]} <span className="text-faint">· {commit.author.name}</span>
981 </p>
982 )}
983 </section>
984
Merge main: Deployments panel in the About, project homepage, both sides' operations985 {checklist && <ProductionChecklist base={base} items={checklist} title={loaderData.checklistTitle} />}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look986
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API987 <Panel
988 title="Right now"
Merge branch 'worktree-agent-ad7c6d88d93adc817'989 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 API990 count={agentsLive.length}
991 all={{ to: `${base}/agents`, label: "Agents" }}
992 >
993 {agentsLive.length > 0 && (
994 <ul className="mb-3 grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3">
995 {agentsLive.map((run) => (
Merge branch 'worktree-agent-ad7c6d88d93adc817'996 <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 API997 <div className="flex items-center gap-2 text-sm">
998 <Avatar name={run.agent} size={18} />
999 <span className="font-medium">{run.agent}</span>
1000 <span className="text-xs text-muted">{RUN_KIND_LABEL[run.kind].toLowerCase()}</span>
1001 <span className="grow" />
1002 <span className="text-xs text-muted">
1003 <Elapsed from={run.startedAt ?? run.createdAt} />
Projects: what a workspace builds and runs, first on every page1004 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1005 </div>
1006 <Link
1007 to={run.number != null ? `${base}/pull/${run.number}` : `${base}/agents/runs/${run.id}`}
1008 className="mt-1 block truncate text-[0.8125rem] hover:text-accent"
1009 >
1010 {run.title ?? "A run"} {run.number != null && <span className="text-faint">#{run.number}</span>}
1011 </Link>
1012 {run.step && (
Merge branch 'worktree-agent-ae1299e92e4462012'1013 <Hint label={run.step}>
1014 <p className="mt-1 truncate font-mono text-[0.6875rem] text-fg/70">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1015 <span className="mr-1.5 inline-block size-1.5 animate-pulse rounded-full bg-accent align-middle" />
Merge branch 'worktree-agent-ae1299e92e4462012'1016 {run.step}
1017 </p>
1018 </Hint>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1019 )}
1020 <p className="mt-1.5 flex items-center gap-3 text-[0.6875rem] text-faint">
1021 {formatCost(run.costUsd) && <span>{formatCost(run.costUsd)} so far</span>}
1022 <Link to={`${base}/agents/runs/${run.id}`} className="hover:text-fg">
1023 {run.stepCount} steps
1024 </Link>
1025 {run.number != null && (
1026 <Link to={`${base}/sessions/${run.number}`} className="hover:text-fg">
1027 Session
1028 </Link>
1029 )}
1030 </p>
1031 </li>
1032 ))}
1033 </ul>
1034 )}
1035 {!loaderData.pullsLoaded ? (
1036 <Unavailable what="Pull requests" />
1037 ) : Object.values(columns).every((c) => c.length === 0) ? (
1038 <Quiet
1039 action={
1040 member ? (
1041 <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-white">
1042 <Bot size={12} /> Open an issue for an agent
1043 </Link>
1044 ) : null
1045 }
1046 >
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1047 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 API1048 </Quiet>
1049 ) : (
1050 <Pipeline columns={columns} base={base} />
Projects: what a workspace builds and runs, first on every page1051 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1052 </Panel>
Projects: what a workspace builds and runs, first on every page1053
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1054 <div className="grid gap-x-8 gap-y-8 lg:grid-cols-[minmax(0,1fr)_20rem]">
1055 <div className="min-w-0 space-y-8">
1056 {needs.length > 0 && (
1057 <Panel title="Needs you" icon={<Hand size={14} className="text-warn" />} count={needs.length}>
1058 <NeedsList needs={needs} limit={5} />
1059 </Panel>
1060 )}
1061
Fast pages, required checks on the branch, self-hosted runners, honest incidents1062 <Suspense
1063 fallback={
1064 <Panel title="Active branches" icon={<GitBranch size={14} />}>
1065 <div aria-busy="true">
1066 <SkeletonRows rows={3} rowClassName="h-12" />
1067 </div>
1068 </Panel>
1069 }
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1070 >
Merge main: Deployments panel in the About, project homepage, both sides' operations1071 <Await
1072 resolve={branches}
1073 errorElement={
1074 <Panel title="Active branches" icon={<GitBranch size={14} />}>
1075 <Unavailable what="Branches" />
1076 </Panel>
1077 }
1078 >
Fast pages, required checks on the branch, self-hosted runners, honest incidents1079 {(branches) => (
Merge main: Deployments panel in the About, project homepage, both sides' operations1080 <Panel
1081 title="Active branches"
1082 icon={<GitBranch size={14} />}
1083 count={branches && branches !== "slow" ? branches.total || null : null}
1084 all={{ to: `${base}/branches`, label: "All branches" }}
1085 >
1086 {branches === "slow" ? (
1087 <Quiet>
1088 Its branches are taking a while to compare.{" "}
1089 <Link to={`${base}/branches`} className="text-accent hover:underline">
1090 See them on Branches
1091 </Link>
1092 .
1093 </Quiet>
1094 ) : !branches ? (
Fast pages, required checks on the branch, self-hosted runners, honest incidents1095 <Unavailable what="Branches" />
1096 ) : branches.shown.length === 0 ? (
1097 <Quiet>
1098 Only {branches.main} so far. Branches pushed here show with how far each has moved from {branches.main}, and
1099 its pull request and preview.
1100 </Quiet>
1101 ) : (
1102 <>
1103 <ActiveBranches branches={branches.shown} base={base} main={branches.main} />
1104 {branches.total > branches.shown.length && (
1105 <p className="mt-2 px-1 text-xs text-faint">
1106 The {branches.shown.length} most recently changed of {branches.total} branches.
1107 </p>
1108 )}
1109 </>
1110 )}
1111 </Panel>
1112 )}
1113 </Await>
1114 </Suspense>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1115
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1116 <Panel title="Recent changes" icon={<GitMerge size={14} />} all={{ to: `${base}/pulls?state=closed`, label: "All landed" }}>
1117 {landed.length === 0 ? (
1118 <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 page1119 ) : (
1120 <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 API1121 {landed.map((pull) => {
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1122 const byAgent = pull.runtime === "hosted" || madeByG1t(pull);
Projects: what a workspace builds and runs, first on every page1123 return (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1124 <li key={pull.id} className="flex items-start gap-3 px-4 py-3">
1125 <GitMerge size={15} className="mt-0.5 shrink-0 text-merged" />
1126 <span className="min-w-0 grow">
1127 <Link to={`${base}/pull/${pull.number}`} prefetch="intent" className="block truncate text-sm font-medium hover:text-accent">
1128 {pull.title}
1129 </Link>
1130 <span className="mt-0.5 flex flex-wrap items-center gap-x-1.5 text-xs text-muted">
1131 <span className="font-mono text-faint">#{pull.number}</span>
1132 <span>·</span>
1133 <span className="inline-flex items-center gap-1">
1134 <Avatar name={pull.agent} size={13} />
g1t is the stored author of what it opens; the person who asked is requested_by and keeps the author's rights1135 {pull.requestedBy
1136 ? `made by ${pull.author.username} for ${pull.requestedBy.username}`
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent1137 : byAgent
1138 ? `made by ${pull.agent}`
1139 : `by ${pull.author.username}`}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1140 </span>
1141 {pull.issue != null && (
1142 <>
1143 <span>· for</span>
1144 <Link to={`${base}/issues/${pull.issue}`} className="hover:text-fg">
1145 #{pull.issue}
1146 </Link>
1147 </>
1148 )}
1149 {pull.mergedBy && <span>· landed by {pull.mergedBy}</span>}
1150 <span>
1151 · <TimeAgo at={pull.mergedAt ?? pull.updatedAt} />
Projects: what a workspace builds and runs, first on every page1152 </span>
1153 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1154 </span>
1155 <span className="hidden shrink-0 text-xs text-faint sm:block">
1156 <ChangeSize files={pull.files} />
1157 </span>
1158 {byAgent && (
1159 <Link to={`${base}/sessions/${pull.number}`} className="shrink-0 text-xs text-muted hover:text-fg">
1160 Session
1161 </Link>
1162 )}
Projects: what a workspace builds and runs, first on every page1163 </li>
1164 );
1165 })}
1166 </ul>
1167 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1168 </Panel>
Projects: what a workspace builds and runs, first on every page1169
Merge main: Deployments panel in the About, project homepage, both sides' operations1170 {loaderData.commits.length > 0 && (
1171 <Panel
1172 title={`Latest on ${source?.defaultBranch ?? "main"}`}
1173 icon={<GitCommitHorizontal size={14} />}
1174 all={{ to: `${base}/commits`, label: "All commits" }}
1175 >
1176 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
1177 {loaderData.commits.map((one) => (
1178 <li key={one.hash} className="flex items-center gap-3 px-4 py-2.5 text-sm">
1179 <Avatar name={one.author} size={18} />
1180 <span className="min-w-0 grow">
1181 <Link to={`${base}/commit/${one.hash}`} className="block truncate hover:text-accent">
1182 {one.message}
1183 </Link>
1184 <span className="text-xs text-muted">{one.author}</span>
1185 </span>
1186 <Link to={`${base}/commit/${one.hash}`} className="hidden shrink-0 font-mono text-xs text-faint hover:text-fg sm:block">
1187 {one.hash.slice(0, 7)}
1188 </Link>
1189 <span className="w-16 shrink-0 text-right text-xs text-faint">
1190 <TimeAgo at={one.at} />
1191 </span>
1192 </li>
1193 ))}
1194 </ul>
1195 </Panel>
1196 )}
1197
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1198 <Panel title="Activity" icon={<ActivityIcon size={14} />}>
1199 {loaderData.eventsLoaded ? (
1200 <ActivityFeed groups={groups} showRepo={false} limit={8} empty="Nothing has happened here yet." />
1201 ) : (
1202 <Unavailable what="Activity" />
1203 )}
1204 </Panel>
1205
1206 {previews.length > 0 && (
1207 <Panel title="Previews" icon={<GitBranch size={14} />} count={previews.length} all={{ to: `${base}/deployments`, label: "Deployments" }}>
1208 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
1209 {previews.map((app) => (
1210 <li key={app.url} className="flex items-center gap-3 px-4 py-2.5 text-sm">
1211 <span className="min-w-0 grow">
1212 <a href={app.url} className="block truncate font-mono text-[0.8125rem] hover:text-accent">
1213 {host(app.url)}
1214 </a>
1215 <span className="text-xs text-muted">
1216 {app.branch}
1217 {app.number != null && (
1218 <>
1219 {" · "}
1220 <Link to={`${base}/pull/${app.number}`} className="hover:underline">
1221 #{app.number}
1222 </Link>
1223 </>
1224 )}
1225 </span>
Projects: what a workspace builds and runs, first on every page1226 </span>
1227 <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 API1228 <TimeAgo at={app.deployedAt} />
Projects: what a workspace builds and runs, first on every page1229 </span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1230 </li>
1231 ))}
1232 </ul>
1233 </Panel>
1234 )}
1235 </div>
Projects: what a workspace builds and runs, first on every page1236
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1237 <aside className="space-y-4 text-sm">
Merge main: Deployments panel in the About, project homepage, both sides' operations1238 {/* Deployments panel (deployments-panel.tsx): each environment's latest deployment. */}
1239 <DeploymentsPanel base={base} summary={loaderData.environments} className="rounded-xl border border-line bg-surface p-5" />
1240 {project && (
1241 <section className="rounded-xl border border-line bg-surface p-5">
1242 <div className="flex items-center justify-between">
1243 <h2 className="text-sm font-semibold">About</h2>
1244 {canChange && <AboutEditor project={project} />}
1245 </div>
1246 <p className={`mt-2.5 text-sm ${project.description ? "text-fg-soft" : "text-faint"}`}>
1247 {project.description ?? "No description."}
1248 </p>
1249 <LinkList links={project.links} className="mt-3" />
1250 <ul className="mt-4 space-y-2 text-xs text-muted">
1251 <li className="flex items-center gap-2">
1252 {project.kind === "library" || project.kind === "tool" ? (
1253 <PackageGlyph size={13} className="shrink-0 text-faint" />
1254 ) : project.kind === "docs" ? (
1255 <BookOpen size={13} className="shrink-0 text-faint" />
1256 ) : (
1257 <Rocket size={13} className="shrink-0 text-faint" />
1258 )}
1259 <Hint label={project.kindReason.detail}>
1260 <span>{kindLabel(project)}</span>
1261 </Hint>
1262 </li>
1263 {loaderData.release && (
1264 <li className="flex items-center gap-2">
1265 <Tag size={13} className="shrink-0 text-faint" />
1266 <Link to={`${base}/tags`} className="hover:text-fg">
1267 <span className="font-mono text-fg-soft">{loaderData.release.name}</span>
1268 {loaderData.release.at && (
1269 <span className="text-faint">
1270 {" · "}
1271 <TimeAgo at={loaderData.release.at} />
1272 </span>
1273 )}
1274 </Link>
1275 </li>
1276 )}
1277 {loaderData.languages && loaderData.languages.length > 0 && (
1278 <li className="flex items-center gap-2">
1279 <Code2 size={13} className="shrink-0 text-faint" />
1280 <span>
1281 {loaderData.languages
1282 .slice(0, 3)
1283 .map((language) => `${language.name} ${Math.round(language.share * 100)}%`)
1284 .join(" · ")}
1285 </span>
1286 </li>
1287 )}
1288 </ul>
1289 {loaderData.contributors && loaderData.contributors.length > 0 && (
1290 <div className="mt-4 flex flex-wrap gap-1">
1291 {loaderData.contributors.slice(0, 12).map((person) => (
1292 <Hint key={person.username} label={person.username}>
1293 <Link to={`/${person.username}`}>
1294 <Avatar name={person.username} size={22} />
1295 </Link>
1296 </Hint>
1297 ))}
1298 </div>
1299 )}
1300 </section>
1301 )}
1302
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1303 {member && (
1304 <section className="rounded-xl border border-line bg-surface p-5">
1305 <div className="flex items-center justify-between">
1306 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1307 <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 API1308 What agents know here
1309 </h2>
1310 <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg">
1311 Memory
1312 </Link>
Projects: what a workspace builds and runs, first on every page1313 </div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1314 {!loaderData.memoriesLoaded ? (
1315 <p className="mt-3 text-xs text-muted">Memory could not be loaded just now.</p>
1316 ) : knows.length === 0 ? (
1317 <p className="mt-3 text-xs leading-5 text-muted">
1318 Nothing yet. Agents note conventions, decisions and traps as they work, and every agent starting here reads them.
1319 </p>
1320 ) : (
1321 <ul className="mt-3 space-y-2.5">
1322 {knows.map((memory) => (
1323 <li key={memory.id} className="text-xs leading-5">
1324 <p className="line-clamp-3 text-fg-soft">
1325 {memory.pinned && <Pin size={11} className="mr-1 inline text-accent" />}
Usage, Billing settings and prepaid AI credit; fixes from the UX audit1326 <InlineMarkdown text={memory.text} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1327 </p>
1328 <p className="text-faint">
1329 {memory.kind} · {memory.createdBy} · <TimeAgo at={memory.createdAt} />
1330 </p>
1331 </li>
1332 ))}
1333 {loaderData.memoryCount > knows.length && (
1334 <li>
1335 <Link to={`${base}/memory`} className="text-xs text-muted hover:text-fg">
1336 {loaderData.memoryCount - knows.length} more
1337 </Link>
1338 </li>
1339 )}
1340 </ul>
1341 )}
1342 </section>
Projects: what a workspace builds and runs, first on every page1343 )}
1344
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1345 <section className="rounded-xl border border-line bg-surface p-5">
1346 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
1347 <HeartPulse size={14} className="text-faint" />
1348 Health
Project dependencies: addresses, preview stacks, Affects, and agents who know1349 </h2>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1350 <div className="mt-4 space-y-4 text-xs">
1351 <div>
1352 <div className="flex items-baseline justify-between">
1353 <span className="text-muted">Checks passing</span>
1354 <span className="font-medium tabular-nums">{percent(health.passRate)}</span>
1355 </div>
1356 <div className="mt-1.5">
Merge branch 'worktree-agent-ad7c6d88d93adc817'1357 <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 API1358 </div>
1359 <p className="mt-1 text-faint">
1360 {health.checkRuns
1361 ? `${health.checkRuns} recent runs · ${percent(health.firstPass.rate)} pass on the first try`
1362 : "No checks have run recently."}
1363 </p>
1364 </div>
Merge main: Deployments panel in the About, project homepage, both sides' operations1365 {member && (plan === "production" || builds.length > 0) && (
Project dependencies: addresses, preview stacks, Affects, and agents who know1366 <div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1367 <div className="flex items-baseline justify-between">
1368 <span className="text-muted">Deploys</span>
1369 <Link to={`${base}/deployments`} className="text-faint hover:text-fg">
1370 {builds.length ? `last ${Math.min(builds.length, 20)}` : "none yet"}
1371 </Link>
1372 </div>
1373 <div className="mt-1.5">
1374 <DeployStrip builds={builds} base={base} />
1375 </div>
Project dependencies: addresses, preview stacks, Affects, and agents who know1376 </div>
1377 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1378 <div>
1379 <div className="flex items-baseline justify-between">
1380 <span className="text-muted">Open issues by age</span>
1381 <Link to={`${base}/issues`} className="font-medium tabular-nums hover:text-accent">
1382 {open.issues}
1383 </Link>
1384 </div>
1385 {ages ? (
1386 <ul className="mt-2 space-y-1.5">
1387 {ages.map((bucket) => (
1388 <li key={bucket.label} className="grid grid-cols-[6.5rem_1fr_1.5rem] items-center gap-2">
1389 <span className="text-faint">{bucket.label}</span>
1390 <span className="block h-1.5 overflow-hidden rounded-full bg-line">
1391 <span
1392 className={`block h-full rounded-full ${bucket.label === "Older" ? "bg-warn" : "bg-fg-soft/60"}`}
1393 style={{ width: `${(bucket.count / oldest) * 100}%` }}
1394 />
1395 </span>
1396 <span className="text-right tabular-nums text-muted">{bucket.count}</span>
Project dependencies: addresses, preview stacks, Affects, and agents who know1397 </li>
1398 ))}
1399 </ul>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1400 ) : (
1401 <p className="mt-1 text-faint">Issues could not be loaded just now.</p>
1402 )}
1403 </div>
Project dependencies: addresses, preview stacks, Affects, and agents who know1404 </div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1405 </section>
Project dependencies: addresses, preview stacks, Affects, and agents who know1406
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1407 <section className="rounded-xl border border-line bg-surface p-5">
1408 <div className="flex items-center justify-between">
1409 <h2 className="flex items-center gap-1.5 text-sm font-semibold">
1410 <Network size={14} className="text-faint" />
1411 Dependencies
1412 </h2>
1413 {member && (
1414 <Link to={`${base}/settings/dependencies`} className="text-xs text-muted hover:text-fg">
1415 Manage
1416 </Link>
1417 )}
1418 </div>
1419 {!dependencies ? (
1420 <p className="mt-3 text-xs text-muted">Dependencies could not be loaded just now.</p>
1421 ) : dependencies.dependsOn.length === 0 && dependencies.usedBy.length === 0 ? (
1422 <p className="mt-3 text-xs leading-5 text-muted">
1423 Uses no other project, and none uses it. Declare one, and builds get its address and agents know what depends on
1424 what.
1425 </p>
1426 ) : (
1427 <div className="mt-3 space-y-3 text-xs">
1428 {dependencies.dependsOn.length > 0 && (
1429 <div>
1430 <p className="flex items-center gap-1 text-muted">
1431 <ArrowUpRight size={12} /> Uses
1432 </p>
1433 <ul className="mt-1.5 space-y-1">
1434 {dependencies.dependsOn.map((d) => (
1435 <li key={d.slug} className="flex items-center justify-between gap-2">
1436 <Link to={`/${params.owner}/${d.slug}`} className="font-medium hover:underline">
1437 {d.name}
1438 </Link>
1439 {d.as && <code className="font-mono text-faint">{d.as}</code>}
1440 </li>
1441 ))}
1442 </ul>
1443 </div>
1444 )}
1445 {dependencies.usedBy.length > 0 && (
1446 <div>
1447 <p className="flex items-center gap-1 text-muted">
1448 <ArrowDownLeft size={12} /> Used by
1449 </p>
1450 <ul className="mt-1.5 space-y-1">
1451 {dependencies.usedBy.map((d) => (
1452 <li key={d.slug}>
1453 <Link to={`/${params.owner}/${d.slug}`} className="font-medium hover:underline">
1454 {d.name}
1455 </Link>
1456 </li>
1457 ))}
1458 </ul>
1459 </div>
1460 )}
1461 </div>
1462 )}
Projects: what a workspace builds and runs, first on every page1463 </section>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1464
1465 {source && (
1466 <section className="rounded-xl border border-line bg-surface p-5">
1467 <h2 className="text-sm font-semibold">Clone</h2>
1468 <div className="mt-3">
Merge branch 'worktree-agent-aaf03bdceac799c89'1469 <CopyLine 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 API1470 </div>
1471 </section>
1472 )}
1473 </aside>
1474 </div>
Projects: what a workspace builds and runs, first on every page1475 </div>
1476 );
1477}

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