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.
| Chat and workspace agents: channels, DMs and named agents you talk to | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| 4 | import type { User } from "@g1t/contracts"; | |
| 5 | ||
| 6 | import { canManage, canSee } from "./access.ts"; | |
| 7 | import { DEFAULT_AUTONOMY, DEFAULT_CAPACITY, applyChanges, readJson } from "./definition.ts"; | |
| 8 | import { TEMPLATES, TEMPLATE_IDS } from "./templates.ts"; | |
| 9 | ||
| 10 | const input = { handle: "Ship", display_name: " Ship ", role: "Release manager", instructions: "Cut releases." }; | |
| 11 | ||
| 12 | test("a new agent gets safe defaults", () => { | |
| 13 | const made = applyChanges(null, input, TEMPLATE_IDS); | |
| 14 | assert.ok(made.ok); | |
| 15 | if (!made.ok) return; | |
| 16 | assert.equal(made.value.handle, "ship"); | |
| 17 | assert.equal(made.value.display_name, "Ship"); | |
| 18 | assert.equal(made.value.personality_preset, "crisp"); | |
| 19 | assert.equal(made.value.capacity, DEFAULT_CAPACITY); | |
| 20 | assert.deepEqual(made.value.autonomy, DEFAULT_AUTONOMY); | |
| 21 | assert.equal(made.value.autonomy.merge, "approval"); | |
| 22 | assert.deepEqual(made.value.routing, { floor: null, ceiling: null, providers: [], pinned: null }); | |
| 23 | }); | |
| 24 | ||
| 25 | test("a new agent needs a handle, a name, a role and instructions", () => { | |
| 26 | for (const missing of ["handle", "display_name", "role", "instructions"] as const) { | |
| 27 | const made = applyChanges(null, { ...input, [missing]: "" }, TEMPLATE_IDS); | |
| 28 | assert.equal(made.ok, false, missing); | |
| 29 | } | |
| 30 | assert.equal(applyChanges(null, { ...input, handle: "g1t" }, TEMPLATE_IDS).ok, false); | |
| 31 | }); | |
| 32 | ||
| 33 | test("a change merges onto the definition, and is checked", () => { | |
| 34 | const made = applyChanges(null, input, TEMPLATE_IDS); | |
| 35 | assert.ok(made.ok); | |
| 36 | if (!made.ok) return; | |
| 37 | const changed = applyChanges(made.value, { routing: { floor: "large" }, budget: { monthly_micros: 5_000_000 }, autonomy: { merge: "never" } }, TEMPLATE_IDS); | |
| 38 | assert.ok(changed.ok); | |
| 39 | if (!changed.ok) return; | |
| 40 | assert.equal(changed.value.handle, "ship", "untouched fields stay"); | |
| 41 | assert.equal(changed.value.routing.floor, "large"); | |
| 42 | assert.equal(changed.value.routing.ceiling, null); | |
| 43 | assert.equal(changed.value.budget.monthly_micros, 5_000_000); | |
| 44 | assert.equal(changed.value.budget.daily_micros, null); | |
| 45 | assert.equal(changed.value.autonomy.merge, "never"); | |
| 46 | assert.equal(changed.value.autonomy.open_pull_requests, "alone"); | |
| 47 | ||
| 48 | const bad = (changes: object) => applyChanges(made.value, changes, TEMPLATE_IDS).ok; | |
| 49 | assert.equal(bad({ routing: { floor: "frontier", ceiling: "small" } }), false, "floor above ceiling"); | |
| 50 | assert.equal(bad({ routing: { floor: "huge" } }), false); | |
| 51 | assert.equal(bad({ routing: { pinned: "no-slash" } }), false); | |
| 52 | assert.equal(bad({ budget: { monthly_micros: -1 } }), false); | |
| 53 | assert.equal(bad({ budget: { daily_micros: 1.5 } }), false); | |
| 54 | assert.equal(bad({ autonomy: { deploy_production: "alone" } }), false, "production deploys always ask"); | |
| 55 | assert.equal(bad({ capacity: 0 }), false); | |
| 56 | assert.equal(bad({ capacity: 11 }), false); | |
| 57 | assert.equal(bad({ personality_preset: "pirate" }), false); | |
| 58 | assert.equal(bad({ template: "nope" }), false); | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 59 | assert.equal(bad({ template: "qa" }), true); |
| Chat and workspace agents: channels, DMs and named agents you talk to | 60 | for (const providers of [[], ["g1t"], ["workspace"], ["int_1"], ["g1t", "workspace"]]) assert.equal(bad({ routing: { providers } }), true, JSON.stringify(providers)); |
| 61 | assert.equal(bad({ routing: { providers: [""] } }), false); | |
| 62 | assert.equal(bad({ instructions: "x".repeat(8001) }), false); | |
| 63 | }); | |
| 64 | ||
| 65 | test("a stored JSON column that cannot be read is the default", () => { | |
| 66 | assert.deepEqual(readJson("{bad", { a: 1 }), { a: 1 }); | |
| 67 | assert.deepEqual(readJson('{"b":2}', { a: 1 }), { a: 1, b: 2 }); | |
| 68 | assert.deepEqual(readJson(null, { a: 1 }), { a: 1 }); | |
| 69 | }); | |
| 70 | ||
| 71 | test("every template is a valid definition with the routing its job needs", () => { | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 72 | assert.deepEqual(TEMPLATE_IDS, ["engineering", "qa", "operations", "docs", "product", "support", "sales"]); |
| Chat and workspace agents: channels, DMs and named agents you talk to | 73 | for (const template of TEMPLATES) { |
| 74 | const made = applyChanges(null, { ...template, template: template.id }, TEMPLATE_IDS); | |
| 75 | assert.ok(made.ok, template.id); | |
| 76 | } | |
| 77 | const byId = Object.fromEntries(TEMPLATES.map((t) => [t.id, t])); | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 78 | assert.equal(byId.qa.routing.floor, "large", "careful review never runs below large"); |
| 79 | assert.equal(byId.product.routing.ceiling, "large", "intake never runs above it"); | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 80 | assert.equal(new Set(TEMPLATES.map((t) => t.handle)).size, TEMPLATES.length, "handles are distinct"); |
| 81 | }); | |
| 82 | ||
| 83 | test("members see a workspace's agents; owners change them", () => { | |
| 84 | const person = (role: "owner" | "member"): User => ({ id: "u", username: "u", workspaces: [{ slug: "acme", role }] }) as User; | |
| 85 | assert.ok(canSee(person("member"), "ACME")); | |
| 86 | assert.ok(!canManage(person("member"), "acme")); | |
| 87 | assert.ok(canManage(person("owner"), "acme")); | |
| 88 | assert.ok(!canSee(person("owner"), "other")); | |
| 89 | assert.ok(!canSee(null, "acme")); | |
| 90 | const token = { id: "w", username: "acme", kind: "workspace" } as User; | |
| 91 | assert.ok(canManage(token, "acme"), "the workspace's own token"); | |
| 92 | assert.ok(!canSee(token, "other")); | |
| 93 | }); | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 94 | |
| 95 | test("templates are named like teammates, with more names to shuffle, each a valid handle", async () => { | |
| 96 | const { checkHandle } = await import("./handle.ts"); | |
| 97 | const byId = Object.fromEntries(TEMPLATES.map((t) => [t.id, t])); | |
| 98 | assert.equal(byId.qa.display_name, "Margo"); | |
| 99 | assert.equal(byId.qa.handle, "margo"); | |
| 100 | for (const template of TEMPLATES) { | |
| 101 | assert.ok(!/reviewer|planner|triage|implementer|documenter|on-call|release/i.test(template.display_name), `${template.id} has a name, not a job title`); | |
| 102 | assert.ok(template.name_ideas.length >= 6 && template.name_ideas.length <= 10, template.id); | |
| 103 | assert.ok(template.name_ideas.includes(template.display_name), `${template.id}'s own name is among its ideas`); | |
| 104 | for (const name of template.name_ideas) assert.ok(checkHandle(name.toLowerCase()).ok, `${name} works as a handle`); | |
| 105 | assert.equal(new Set(template.name_ideas).size, template.name_ideas.length, `${template.id} repeats no name`); | |
| 106 | } | |
| 107 | }); | |
| 108 | ||
| 109 | test("an agent's avatar seed starts as its handle, can be changed, and stays through a rename", () => { | |
| 110 | const made = applyChanges(null, input, TEMPLATE_IDS); | |
| 111 | assert.ok(made.ok); | |
| 112 | if (!made.ok) return; | |
| 113 | assert.equal(made.value.avatar_seed, "ship"); | |
| 114 | const renamed = applyChanges(made.value, { handle: "skipper" }, TEMPLATE_IDS); | |
| 115 | assert.ok(renamed.ok); | |
| 116 | if (renamed.ok) assert.equal(renamed.value.avatar_seed, "ship", "a new name keeps the face"); | |
| 117 | const reseeded = applyChanges(made.value, { avatar_seed: "ship-2" }, TEMPLATE_IDS); | |
| 118 | assert.ok(reseeded.ok); | |
| 119 | if (reseeded.ok) assert.equal(reseeded.value.avatar_seed, "ship-2"); | |
| 120 | assert.equal(applyChanges(made.value, { avatar_seed: "x".repeat(65) }, TEMPLATE_IDS).ok, false); | |
| 121 | const chosen = applyChanges(null, { ...input, avatar_seed: "blob" }, TEMPLATE_IDS); | |
| 122 | assert.ok(chosen.ok && chosen.value.avatar_seed === "blob"); | |
| 123 | }); | |
| 124 | ||
| 125 | test("an agent is hired into a role: title, team or department, responsibilities", async () => { | |
| 126 | const { roleOf } = await import("./definition.ts"); | |
| 127 | const made = applyChanges(null, { handle: "margo", display_name: "Margo", title: "QA Engineer", department: "QA", instructions: "Test things.", responsibilities: ["Review pull requests", "Chase flaky checks"] }, TEMPLATE_IDS); | |
| 128 | assert.ok(made.ok); | |
| 129 | if (!made.ok) return; | |
| 130 | assert.equal(made.value.role, "QA Engineer, QA", "the one-line role is made from the title and department"); | |
| 131 | const onTeam = applyChanges(made.value, { team: "QA-Core" }, TEMPLATE_IDS); | |
| 132 | assert.ok(onTeam.ok && onTeam.value.role === "QA Engineer on the qa-core team", "a made role follows the title and team"); | |
| 133 | const written = applyChanges(made.value, { role: "Keeps us honest" }, TEMPLATE_IDS); | |
| 134 | assert.ok(written.ok); | |
| 135 | if (!written.ok) return; | |
| 136 | const kept = applyChanges(written.value, { title: "QA Lead" }, TEMPLATE_IDS); | |
| 137 | assert.ok(kept.ok && kept.value.role === "Keeps us honest", "a written role is kept"); | |
| 138 | assert.equal(roleOf({ title: "", team: null, department: "QA" }), ""); | |
| 139 | assert.equal(applyChanges(null, { handle: "x1", display_name: "X", instructions: "y" }, TEMPLATE_IDS).ok, false, "a title or a role is needed"); | |
| 140 | const bad = (changes: object) => applyChanges(made.value, changes, TEMPLATE_IDS).ok; | |
| 141 | assert.equal(bad({ responsibilities: ["Only one"] }), false, "2 to 8 duties"); | |
| 142 | assert.equal(bad({ responsibilities: Array.from({ length: 9 }, (_, i) => `Duty ${i}`) }), false); | |
| 143 | assert.equal(bad({ responsibilities: [] }), true, "or none yet"); | |
| 144 | assert.equal(bad({ team: "not a slug!" }), false); | |
| 145 | assert.equal(bad({ team: null }), true); | |
| 146 | }); | |
| 147 | ||
| 148 | test("subagents: named, described, at most 8, never wider than their agent", () => { | |
| 149 | const base = applyChanges(null, { ...input, routing: { floor: "large", ceiling: "large" } }, TEMPLATE_IDS); | |
| 150 | assert.ok(base.ok); | |
| 151 | if (!base.ok) return; | |
| 152 | const helper = { name: "flake-hunter", description: "Bisects flaky tests", instructions: "Find the cause.", routing: { floor: "small" as const, ceiling: "frontier" as const }, max_parallel: 3 }; | |
| 153 | const withHelper = applyChanges(base.value, { subagents: [helper] }, TEMPLATE_IDS); | |
| 154 | assert.ok(withHelper.ok); | |
| 155 | if (!withHelper.ok) return; | |
| 156 | assert.deepEqual(withHelper.value.subagents[0].routing, { floor: "large", ceiling: "large" }, "held within the agent's limits"); | |
| 157 | const loosened = applyChanges(withHelper.value, { routing: { floor: null, ceiling: null } }, TEMPLATE_IDS); | |
| 158 | assert.ok(loosened.ok && loosened.value.subagents[0].routing.floor === "large", "loosening the agent later does not widen what was stored"); | |
| 159 | const bad = (subagents: unknown) => applyChanges(base.value, { subagents: subagents as never }, TEMPLATE_IDS).ok; | |
| 160 | assert.equal(bad([{ ...helper, name: "Flake Hunter" }]), false); | |
| 161 | assert.equal(bad([helper, helper]), false, "names are unique"); | |
| 162 | assert.equal(bad([{ ...helper, description: "" }]), false); | |
| 163 | assert.equal(bad([{ ...helper, max_parallel: 9 }]), false); | |
| 164 | assert.equal(bad(Array.from({ length: 9 }, (_, i) => ({ ...helper, name: `helper-${i}` }))), false, "at most 8"); | |
| 165 | assert.equal(bad([{ ...helper, routing: { floor: "frontier", ceiling: "small" } }]), false); | |
| 166 | }); | |
| 167 | ||
| 168 | test("agents face the workspace's own people; customer-facing ones aren't available yet", () => { | |
| 169 | const made = applyChanges(null, input, TEMPLATE_IDS); | |
| 170 | assert.ok(made.ok && made.value.faces === "internal"); | |
| 171 | const customers = applyChanges(null, { ...input, faces: "customers" }, TEMPLATE_IDS); | |
| 172 | assert.ok(!customers.ok && customers.message === "Customer-facing agents aren't available yet."); | |
| 173 | }); | |
| 174 | ||
| 175 | test("role templates by department, each with a title, duties and a subagent or two; David is back office", () => { | |
| 176 | const byId = Object.fromEntries(TEMPLATES.map((t) => [t.id, t])); | |
| 177 | for (const template of TEMPLATES) { | |
| 178 | assert.ok(template.title && template.department, template.id); | |
| 179 | assert.ok(template.responsibilities.length >= 2 && template.responsibilities.length <= 8, template.id); | |
| 180 | assert.ok(template.subagents.length >= 1 && template.subagents.length <= 2, template.id); | |
| 181 | } | |
| 182 | assert.equal(byId.sales.display_name, "David"); | |
| 183 | assert.equal(byId.sales.title, "Sales Operations"); | |
| 184 | assert.equal(byId.sales.department, "Sales"); | |
| 185 | assert.match(byId.sales.instructions, /never contact a customer/i); | |
| 186 | assert.match(byId.sales.instructions, /never promise roadmap/i); | |
| 187 | assert.match(byId.sales.instructions, /audience/); | |
| 188 | assert.ok(!TEMPLATE_IDS.includes("planner"), "planning is g1t's own job"); | |
| 189 | }); |
This file's history is long; its oldest lines are credited to the oldest commit read.