Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

90 linesCodeBlameRaw
1/**
2 * The workspace overview's ground truth, shaped from one reading of the
3 * work (lib/signals.ts) for components/ground-truth.tsx: the pulse, spend
4 * threaded to the work, each team's last two weeks, what waits on a first
5 * review, and the quiet changes, each list in sentences. Imports only types
6 * and pure functions, so the page's parts can be drawn and tested alone.
7 */
8import type { SignalData } from "./signals.server";
9import {
10 type Group,
11 dailySignal,
12 dayList,
13 harnessWords,
14 namesWords,
15 newGround,
16 newHabits,
17 providerQuarter,
18 quarterSpend,
19 rhythm,
20 silos,
21 spendFlow,
22 spoken,
23 spreading,
24 waitingRows,
25} from "./signals";
26
27/** Entries each quiet change lists. */
28export const QUIET_ROWS = 4;
29
30/** What the rhythm's rows are: teams by their projects, else the projects. Null when identity did not answer. */
31export type Groups = { groups: Group[]; by: "team" | "project" } | null;
32
33export function shapeGround(found: SignalData, groups: Groups, now: number) {
34 const name = (id: string) => found.repos[id]?.name ?? "a project";
35 const path = (id: string) => (found.repos[id] ? `/${found.repos[id]!.namespace}/${found.repos[id]!.name}` : null);
36 const quarter = quarterSpend({ repos: found.repos, signals: found.signals, quarterUsage: found.quarterUsage, quarter: found.range.quarter });
37 // Practices catching on in each team (else across the workspace), the newest first.
38 const scopes =
39 groups?.by === "team"
40 ? groups.groups.map((group) => ({ key: group.key, label: group.label, only: new Set(group.repoIds) as ReadonlySet<string> | undefined }))
41 : [{ key: "all", label: "the workspace", only: undefined }];
42 const spreads = scopes.flatMap((scope) => spreading(found.signals.harnesses, now, { only: scope.only }).map((spread) => ({ scope, spread })));
43 return {
44 tiles: dailySignal(found).tiles,
45 complete: found.complete,
46 capped: found.signals.pullsCapped,
47 flow: spendFlow({ slug: found.slug, repos: found.repos, signals: found.signals, quarterUsage: found.quarterUsage, quarter: found.range.quarter }),
48 sized: (quarter?.total ?? 0) > 0 ? ("spend" as const) : ("pulls" as const),
49 quarterSpend: quarter?.total ?? null,
50 sessionSpend: quarter?.sessions ?? 0,
51 rhythm: groups ? { by: groups.by, rows: rhythm(found.signals.days, dayList(found.range.until, found.range.days), groups.groups) } : null,
52 waiting: waitingRows(found.signals.waiting, found.repos).map((pull) => ({ ...pull, age: spoken(Math.max(0, (now - Date.parse(pull.createdAt)) / 1000)) })),
53 silos: silos(found.signals.authors, now)
54 .slice(0, QUIET_ROWS)
55 .map((silo) => ({
56 key: silo.repoId,
57 to: path(silo.repoId),
58 where: name(silo.repoId),
59 line: `${silo.name} merged ${silo.theirs} of its ${silo.recent} changes in 90 days`,
60 more: silo.joining.length > 0 ? `${silo.joining.map((who) => who.name).join(", ")} started this month` : "No one else has started there",
61 })),
62 newGround: newGround(found.signals.authors, now)
63 .slice(0, QUIET_ROWS)
64 .map((row) => ({
65 key: `${row.ownerId}:${row.repoId}`,
66 to: path(row.repoId),
67 where: name(row.repoId),
68 line: `${row.name} merged a first change here`,
69 more: `${row.elsewhere} merges elsewhere first`,
70 })),
71 spreading: spreads.slice(0, QUIET_ROWS).map(({ scope, spread }) => ({
72 key: `${scope.key}:${spread.harness}`,
73 to: null,
74 where: `${harnessWords(spread.harness)} in ${scope.label}`,
75 line: `${namesWords(spread.recent.map((person) => person.name))} started using it this month, after ${spread.first.name}`,
76 more: `${spread.people} people use it now`,
77 })),
78 habits: newHabits(found.signals.pulls, now)
79 .slice(0, QUIET_ROWS)
80 .map((row) => ({
81 key: row.ownerId,
82 to: path(row.repoId),
83 where: name(row.repoId),
84 line: `${row.name} merged a first agent-made change`,
85 more: `after ${row.byHand} by hand`,
86 })),
87 };
88}
89
90export type Ground = ReturnType<typeof shapeGround> & { providers: ReturnType<typeof providerQuarter> };