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"); | |
| People and teams are front and centre: one directory of people and agents with presence, local time, titles, teams and what each owns; profiles with manager and reports and the agents they work with; an org chart with each team's agents beside the person who leads it; and teams of any mix, with a lead, a channel, a budget agents keep to and the agents on them. Every agent is told its teams each turn (who leads, who owns what, who's around and who to page), and the team page shows exactly what. Member management is Members and invites; the people and teams guide says how. | 123 | assert.equal(teamCounts({ members_count: 2, repos_count: 1, child_teams_count: 0 }, 1), "2 members · 1 agent · 1 repository"); |
| 124 | assert.equal(teamCounts({ members_count: 0, repos_count: 0, child_teams_count: 0 }, 3), "3 agents"); | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 125 | }); |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 126 | |
| 127 | test("who may create teams follows the workspace's setting", async () => { | |
| 128 | // From the contracts' source: the Teams page and the + menu decide with it. | |
| 129 | const { mayCreateTeams } = await import("../../../../packages/contracts/src/teams.ts"); | |
| 130 | assert.equal(mayCreateTeams(undefined, "member"), true); | |
| 131 | assert.equal(mayCreateTeams("members", "member"), true); | |
| 132 | assert.equal(mayCreateTeams("owners", "member"), false); | |
| 133 | assert.equal(mayCreateTeams("owners", "owner"), true); | |
| 134 | assert.equal(mayCreateTeams("members", null), false); | |
| 135 | }); |
This file's history is long; its oldest lines are credited to the oldest commit read.