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.
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| 4 | import type { ReviewAssignment } from "@g1t/contracts"; | |
| 5 | ||
| 6 | import { | |
| 7 | filterTeams, | |
| 8 | newTeamFromForm, | |
| 9 | parentChoices, | |
| 10 | reviewAssignmentFromForm, | |
| 11 | splitTeams, | |
| 12 | teamChangesFromForm, | |
| 13 | teamCounts, | |
| 14 | teamPath, | |
| 15 | } from "./teams.ts"; | |
| 16 | ||
| 17 | const team = (slug: string, extra: Record<string, unknown> = {}) => ({ | |
| 18 | slug, | |
| 19 | name: slug[0].toUpperCase() + slug.slice(1), | |
| 20 | description: null as string | null, | |
| 21 | visibility: "visible" as "visible" | "secret", | |
| 22 | parent: null as { slug: string; name: string } | null, | |
| 23 | viewer_role: null as "member" | "maintainer" | null, | |
| 24 | ...extra, | |
| 25 | }); | |
| 26 | ||
| 27 | const form = (fields: Record<string, string | string[]>) => { | |
| 28 | const data = new FormData(); | |
| 29 | for (const [key, value] of Object.entries(fields)) { | |
| 30 | for (const one of Array.isArray(value) ? value : [value]) data.append(key, one); | |
| 31 | } | |
| 32 | return data; | |
| 33 | }; | |
| 34 | ||
| 35 | const DEFAULTS: ReviewAssignment = { | |
| 36 | enabled: false, | |
| 37 | algorithm: "round_robin", | |
| 38 | count: 1, | |
| 39 | skip_busy: false, | |
| 40 | busy_at: 5, | |
| 41 | include_child_teams: false, | |
| 42 | excluded: [], | |
| 43 | notify_team: false, | |
| 44 | }; | |
| 45 | ||
| 46 | test("team pages live under the workspace's teams", () => { | |
| 47 | assert.equal(teamPath("acme", "backend"), "/acme/-/teams/backend"); | |
| 48 | assert.equal(teamPath("acme", "backend", "settings"), "/acme/-/teams/backend/settings"); | |
| 49 | }); | |
| 50 | ||
| 51 | test("teams are found by every word of the search", () => { | |
| 52 | const teams = [team("backend", { description: "APIs and data" }), team("web"), team("platform", { description: "Shared backend tools" })]; | |
| 53 | assert.deepEqual(filterTeams(teams, "").map((t) => t.slug), ["backend", "web", "platform"]); | |
| 54 | assert.deepEqual(filterTeams(teams, "BACKEND").map((t) => t.slug), ["backend", "platform"]); | |
| 55 | assert.deepEqual(filterTeams(teams, "backend tools").map((t) => t.slug), ["platform"]); | |
| 56 | assert.deepEqual(filterTeams(teams, "nothing"), []); | |
| 57 | }); | |
| 58 | ||
| 59 | test("your teams come first", () => { | |
| 60 | const { mine, others } = splitTeams([team("a"), team("b", { viewer_role: "member" }), team("c", { viewer_role: "maintainer" })]); | |
| 61 | assert.deepEqual(mine.map((t) => t.slug), ["b", "c"]); | |
| 62 | assert.deepEqual(others.map((t) => t.slug), ["a"]); | |
| 63 | }); | |
| 64 | ||
| 65 | test("a team can go under any visible team but itself and those below it", () => { | |
| 66 | const teams = [ | |
| 67 | team("eng"), | |
| 68 | team("backend", { parent: { slug: "eng", name: "Eng" } }), | |
| 69 | team("api", { parent: { slug: "backend", name: "Backend" } }), | |
| 70 | team("web", { parent: { slug: "eng", name: "Eng" } }), | |
| 71 | team("secret", { visibility: "secret" }), | |
| 72 | ]; | |
| 73 | assert.deepEqual(parentChoices(teams, "backend").map((t) => t.slug), ["eng", "web"]); | |
| 74 | assert.deepEqual(parentChoices(teams, "eng").map((t) => t.slug), []); | |
| 75 | assert.deepEqual(parentChoices(teams, null).map((t) => t.slug), ["eng", "backend", "api", "web"]); | |
| 76 | }); | |
| 77 | ||
| 78 | test("a new team reads its form", () => { | |
| 79 | assert.deepEqual(newTeamFromForm(form({ name: " Backend ", slug: "", description: "", visibility: "secret", parent: "", members: "@ana, bo cy" })), { | |
| 80 | name: "Backend", | |
| 81 | slug: null, | |
| 82 | description: null, | |
| 83 | visibility: "secret", | |
| 84 | parent: null, | |
| 85 | members: ["ana", "bo", "cy"], | |
| 86 | }); | |
| 87 | assert.equal(newTeamFromForm(form({ name: "x", visibility: "weird" })).visibility, "visible"); | |
| 88 | }); | |
| 89 | ||
| 90 | test("settings change only what their form has", () => { | |
| 91 | assert.deepEqual(teamChangesFromForm(form({ name: "Web", description: " UI " })), { name: "Web", description: "UI" }); | |
| 92 | assert.deepEqual(teamChangesFromForm(form({ parent: "" })), { parent: "" }); | |
| 93 | assert.deepEqual(teamChangesFromForm(form({ "notify-shown": "1" })), { notify: false }); | |
| 94 | assert.deepEqual(teamChangesFromForm(form({ "notify-shown": "1", notify: "on" })), { notify: true }); | |
| 95 | assert.deepEqual(teamChangesFromForm(form({ visibility: "secret" })), { visibility: "secret" }); | |
| 96 | }); | |
| 97 | ||
| 98 | test("review assignment is read within bounds", () => { | |
| 99 | const read = reviewAssignmentFromForm( | |
| 100 | form({ enabled: "on", algorithm: "load_balance", count: "40", skip_busy: "on", busy_at: "0", excluded: "@Ana, bo ana" }), | |
| 101 | DEFAULTS, | |
| 102 | ); | |
| 103 | assert.deepEqual(read, { | |
| 104 | enabled: true, | |
| 105 | algorithm: "load_balance", | |
| 106 | count: 10, | |
| 107 | skip_busy: true, | |
| 108 | busy_at: 1, | |
| 109 | include_child_teams: false, | |
| 110 | excluded: ["ana", "bo"], | |
| 111 | notify_team: false, | |
| 112 | }); | |
| 113 | // Off, and nonsense kept as it was. | |
| 114 | const off = reviewAssignmentFromForm(form({ algorithm: "random", count: "many" }), { ...DEFAULTS, count: 3 }); | |
| 115 | assert.equal(off.enabled, false); | |
| 116 | assert.equal(off.algorithm, "round_robin"); | |
| 117 | assert.equal(off.count, 3); | |
| 118 | }); | |
| 119 | ||
| 120 | test("counts leave out what is none", () => { | |
| 121 | assert.equal(teamCounts({ members_count: 1, repos_count: 0, child_teams_count: 2 }), "1 member · 2 child teams"); | |
| 122 | assert.equal(teamCounts({ members_count: 0, repos_count: 0, child_teams_count: 0 }), "No members yet"); | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 123 | assert.equal(teamCounts({ members_count: 2, repos_count: 1, child_teams_count: 0 }), "2 members · 1 repository"); |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 124 | }); |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 125 | |
| 126 | test("who may create teams follows the workspace's setting", async () => { | |
| 127 | // From the contracts' source: the Teams page and the + menu decide with it. | |
| 128 | const { mayCreateTeams } = await import("../../../../packages/contracts/src/teams.ts"); | |
| 129 | assert.equal(mayCreateTeams(undefined, "member"), true); | |
| 130 | assert.equal(mayCreateTeams("members", "member"), true); | |
| 131 | assert.equal(mayCreateTeams("owners", "member"), false); | |
| 132 | assert.equal(mayCreateTeams("owners", "owner"), true); | |
| 133 | assert.equal(mayCreateTeams("members", null), false); | |
| 134 | }); |
This file's history is long; its oldest lines are credited to the oldest commit read.