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.
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| 4 | import type { AuditEntry } from "@g1t/contracts"; | |
| 5 | ||
| 6 | import { | |
| 7 | actorLabel, | |
| 8 | exportName, | |
| 9 | filterHref, | |
| 10 | parseFilters, | |
| 11 | retainedSince, | |
| 12 | ruleLabel, | |
| 13 | targetLabel, | |
| 14 | toCsv, | |
| 15 | toQuery, | |
| 16 | visibilityFor, | |
| 17 | } from "./audit.ts"; | |
| 18 | ||
| 19 | const entry: AuditEntry = { | |
| 20 | id: "aud_1", | |
| 21 | time: "2026-10-04T12:00:00.000Z", | |
| 22 | actorKind: "agent", | |
| 23 | actor: "g1t", | |
| 24 | actorId: "usr_g1t_agent", | |
| 25 | agent: "g1t", | |
| 26 | onBehalfOf: "syntaqx", | |
| 27 | runId: "run_1", | |
| 28 | runKind: "implement", | |
| 29 | credentialId: "tok_1", | |
| 30 | action: "merge_pull_request", | |
| 31 | surface: "mcp", | |
| 32 | workspace: "acme", | |
| 33 | repo: "acme/rocket", | |
| 34 | number: 12, | |
| 35 | gitRef: null, | |
| 36 | path: null, | |
| 37 | outcome: "denied", | |
| 38 | rule: "never", | |
| 39 | result: "forbidden", | |
| 40 | message: 'g1t\'s token can never use merge_pull_request: "merging" is for people, too.', | |
| 41 | requestId: "8c1f", | |
| 42 | }; | |
| 43 | ||
| 44 | test("owners see everything, members their projects, others nothing", () => { | |
| 45 | assert.deepEqual(visibilityFor("owner", "ana"), { kind: "all" }); | |
| 46 | assert.deepEqual(visibilityFor("member", "ana"), { kind: "projects", username: "ana" }); | |
| 47 | assert.equal(visibilityFor(null, "ana"), null); | |
| 48 | }); | |
| 49 | ||
| 50 | test("filters are read from the address, and nonsense is dropped", () => { | |
| 51 | const filters = parseFilters( | |
| 52 | new URLSearchParams("actor=syntaqx&outcome=maybe&kind=agent&from=2026-10-01&to=yesterday&project=rocket"), | |
| 53 | ); | |
| 54 | assert.equal(filters.actor, "syntaqx"); | |
| 55 | assert.equal(filters.outcome, ""); | |
| 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. | 56 | // Agents are no longer a filter: an old link's kind=agent is dropped. |
| 57 | assert.equal(filters.kind, ""); | |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 58 | assert.equal(filters.from, "2026-10-01"); |
| 59 | assert.equal(filters.to, ""); | |
| 60 | const query = toQuery("acme", { kind: "all" }, { ...filters, to: "2026-10-04" }, 100); | |
| 61 | assert.equal(query.repo, "acme/rocket"); | |
| 62 | assert.equal(query.since, "2026-10-01T00:00:00.000Z"); | |
| 63 | // The end day is inclusive. | |
| 64 | assert.equal(query.until, "2026-10-05T00:00:00.000Z"); | |
| 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. | 65 | assert.equal(query.actorKind, null); |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 66 | assert.equal(query.outcome, null); |
| 67 | }); | |
| 68 | ||
| 69 | test("links keep the other filters", () => { | |
| 70 | const filters = parseFilters(new URLSearchParams("actor=ana&outcome=denied")); | |
| 71 | assert.equal(filterHref("/acme/-/audit", filters, { before: "aud_9" }), "/acme/-/audit?actor=ana&outcome=denied&before=aud_9"); | |
| 72 | assert.equal(filterHref("/acme/-/audit", parseFilters(new URLSearchParams())), "/acme/-/audit"); | |
| 73 | }); | |
| 74 | ||
| 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. | 75 | test("an old agent's entry is shown with whom it acted for", () => { |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 76 | assert.equal(actorLabel(entry), "g1t on behalf of syntaqx"); |
| 77 | assert.equal(actorLabel({ actor: "ana", agent: null, onBehalfOf: null }), "ana"); | |
| 78 | assert.equal(targetLabel(entry), "acme/rocket#12"); | |
| 79 | assert.equal(targetLabel({ ...entry, number: null, gitRef: "refs/heads/fix" }), "acme/rocket refs/heads/fix"); | |
| 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. | 80 | assert.equal(ruleLabel("never"), "never allowed for agents (an old entry)"); |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 81 | assert.equal(ruleLabel("run:implement/tools"), "implement run tools"); |
| 82 | assert.equal(ruleLabel("run:update/runner:push"), "update run runner (push)"); | |
| 83 | }); | |
| 84 | ||
| 85 | test("the CSV quotes what it must and keeps formulas as text", () => { | |
| 86 | const csv = toCsv([entry, { ...entry, id: "aud_2", path: "=HYPERLINK(1)", message: null }]); | |
| 87 | const lines = csv.trimEnd().split("\r\n"); | |
| 88 | assert.equal(lines.length, 3); | |
| 89 | assert.ok(lines[0].startsWith("id,time,workspace,actorKind,actor")); | |
| 90 | assert.ok(lines[1].includes('"g1t\'s token can never use merge_pull_request: ""merging"" is for people, too."')); | |
| 91 | assert.ok(lines[2].includes("'=HYPERLINK(1)")); | |
| 92 | assert.equal(exportName("acme", "csv", new Date("2026-10-04T23:00:00Z")), "acme-audit-2026-10-04.csv"); | |
| 93 | }); | |
| 94 | ||
| 95 | test("the log reads back only as far as the plan keeps it", () => { | |
| 96 | const now = Date.parse("2026-10-31T00:00:00.000Z"); | |
| 97 | // 90 days on the plan, 7 free. | |
| 98 | assert.equal(retainedSince(null, 90, now), "2026-08-02T00:00:00.000Z"); | |
| 99 | assert.equal(retainedSince(null, 7, now), "2026-10-24T00:00:00.000Z"); | |
| 100 | assert.equal(retainedSince(null, 30, now), "2026-10-01T00:00:00.000Z"); | |
| 101 | assert.equal(retainedSince("2026-01-01T00:00:00.000Z", 30, now), "2026-10-01T00:00:00.000Z"); | |
| 102 | // A later start than the window is kept. | |
| 103 | assert.equal(retainedSince("2026-10-20T00:00:00.000Z", 30, now), "2026-10-20T00:00:00.000Z"); | |
| 104 | // A longer window, where one is set. | |
| 105 | assert.equal(retainedSince("2026-01-01T00:00:00.000Z", 365, now), "2026-01-01T00:00:00.000Z"); | |
| 106 | }); |
This file's history is long; its oldest lines are credited to the oldest commit read.