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.

g1t.sh
77 linesCodeBlameRaw

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.

Topics sit in About on the files pages, under the description1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { Project, User } from "@g1t/contracts";
5
6import { can, needs, permission } from "../../../packages/contracts/src/access.ts";
7import { NEEDS, repoRef, type Method } from "./access.ts";
8
9const project = (isPrivate: boolean): Project => ({
10 id: "prj_1",
11 workspace: "acme",
12 slug: "web",
13 name: "web",
14 description: null,
15 source: { kind: "hosted", repoId: "repo_web", repo: { namespace: "acme", name: "web" }, rootDir: "", defaultBranch: "main" },
16 private: isPrivate,
17 archived: false,
18 primary: true,
19 createdBy: "ada",
20 createdAt: "2026-10-01T00:00:00Z",
21 updatedAt: "2026-10-01T00:00:00Z",
22});
23
24const person = (extra: Partial<User>): User => ({ id: "u", username: "u", kind: "user", verified: true, workspaces: [], ...extra }) as User;
25const collaborator = (role: "read" | "write" | "admin") => person({ grants: [{ repo_id: "repo_web", workspace: "acme", role }] });
26
27const allowed = (viewer: User | null, method: Method, isPrivate = true) => can(viewer, repoRef(project(isPrivate)), NEEDS[method]);
28
29test("a Read collaborator sees a project's deployments but cannot deploy or change them", () => {
30 const reader = collaborator("read");
31 for (const method of ["settings", "list", "get", "listDomains"] as const) assert.ok(allowed(reader, method), method);
Projects no longer depend on each other: the dependsOn relation, its settings page (old links redirect), the overview card, a pull request's Affects panel and preview stacks, reference variables and agents' notes on what a project uses are gone; the table is dropped in a later deploy32 for (const method of ["redeploy", "takeDown", "updateSettings", "addDomain"] as const) assert.ok(!allowed(reader, method), method);
Topics sit in About on the files pages, under the description33 assert.equal(needs(NEEDS.redeploy), "Needs the Write role or higher.");
34});
35
36test("an outside collaborator with Write can deploy, but settings and domains need Admin", () => {
37 const writer = collaborator("write");
38 assert.ok(allowed(writer, "redeploy"));
39 assert.ok(allowed(writer, "takeDown"));
40 assert.ok(!allowed(writer, "updateSettings"));
41 assert.ok(!allowed(writer, "addDomain"));
42 assert.equal(needs(NEEDS.addDomain), "Needs the Admin role or higher.");
43 assert.ok(allowed(collaborator("admin"), "addDomain"));
44});
45
46test("members follow the workspace's base permission; owners manage everything", () => {
47 const member = person({ workspaces: [{ slug: "acme", role: "member" }] });
48 assert.ok(allowed(member, "redeploy"), "Write by default");
49 assert.ok(!allowed(member, "updateSettings"));
50 const none = person({ workspaces: [{ slug: "acme", role: "member", base_permission: "none" }] });
51 assert.equal(permission(none, repoRef(project(true))), null, "a private project is not found");
52 const owner = person({ workspaces: [{ slug: "acme", role: "owner" }] });
53 assert.ok(allowed(owner, "updateSettings"));
54});
55
56test("anyone can see a public project's deployments, and no more", () => {
57 assert.ok(allowed(null, "list", false));
58 assert.ok(!allowed(null, "redeploy", false));
59 assert.equal(permission(null, repoRef(project(true))), null);
60});
61
Chat, artifacts and notifications no longer serve g1t's own agents. Direct messages and conversation invites take people only, and a space or folio is shared with people and teams. Chat stops waking agents, drops the RPCs agents posted, read, searched and handed off through, and stops answering buttons on agent cards; old cards keep their links. An agent's old messages still show, under its saved name or as "A deleted agent". Artifacts drops the folio and docs RPCs agents read and edited pages with, and an old agent member of a space can still be removed. Notifications drop "an agent is waiting on you". A deployment no longer trusts a pull request with secrets just because g1t's agent opened it, and the sign-up card no longer promises a team of agents.62test("a preview of a change made for someone is trusted as they are", async () => {
Topics sit in About on the files pages, under the description63 const { workOwner } = await import("../../../packages/contracts/src/work.ts");
64 const { trustedOutright } = await import("./access.ts");
65 const g1t: User = { id: "usr_g1t_agent", username: "g1t", kind: "agent", verified: false, workspaces: [] };
66 const syntaqx: User = { id: "usr_1", username: "syntaqx", kind: "user", verified: false, workspaces: [] };
67 // Made for syntaqx: their role decides, as when they were its author.
68 const made = workOwner({ author: g1t, requestedBy: syntaqx });
69 assert.equal(made.username, "syntaqx");
70 assert.equal(trustedOutright(made), false);
Chat, artifacts and notifications no longer serve g1t's own agents. Direct messages and conversation invites take people only, and a space or folio is shared with people and teams. Chat stops waking agents, drops the RPCs agents posted, read, searched and handed off through, and stops answering buttons on agent cards; old cards keep their links. An agent's old messages still show, under its saved name or as "A deleted agent". Artifacts drops the folio and docs RPCs agents read and edited pages with, and an old agent member of a space can still be removed. Notifications drop "an agent is waiting on you". A deployment no longer trusts a pull request with secrets just because g1t's agent opened it, and the sign-up card no longer promises a team of agents.71 // A pull request g1t's former agent opened for nobody is trusted no further than anyone's.
72 assert.equal(trustedOutright(workOwner({ author: g1t, requestedBy: null })), false);
73 // Only g1t's own system work is.
74 assert.equal(trustedOutright({ kind: "system" }), true);
Topics sit in About on the files pages, under the description75 // Anyone's own pull request: they are asked about.
76 assert.equal(trustedOutright(workOwner({ author: syntaqx, requestedBy: null })), false);
77});

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