Skip to content
135 linesCodeBlameRaw
1import { test } from "node:test";
2import assert from "node:assert/strict";
3
4import { assemble } from "./assemble.ts";
5import { extract } from "./extract.ts";
6import { aspirational, bulletsOf, classify, harvestable, planLike, sentencesOf, wholeSentences } from "./harvest.ts";
7
8const ctx = { project: "g1t", siblings: [] };
9
10test("memory comes from docs on how to work here, never plans, feedback or history", () => {
11 for (const path of ["README.md", "AGENTS.md", "CLAUDE.md", "CONTRIBUTING.md"]) {
12 const role = path.startsWith("README") ? "readme" : path.startsWith("CONTRIBUTING") ? "contributing" : "agents";
13 assert.ok(harvestable(path, role), path);
14 }
15 for (const path of ["docs/DEPLOYING.md", "docs/testing.md", "docs/architecture.md", "docs/SELF_HOSTING.md", "runbooks/restore.md", "docs/getting-started.md"]) {
16 assert.ok(harvestable(path, "doc"), path);
17 }
18 for (const path of ["docs/PLAN.md", "docs/CLOUDFLARE_FEEDBACK.md", "docs/CHANGELOG.md", "docs/roadmap.md", "docs/DEMO.md", "docs/INCIDENTS.md", "docs/PERFORMANCE.md", "docs/release-notes.md"]) {
19 assert.ok(!harvestable(path, "doc"), path);
20 }
21 // project.yml adds a doc, or leaves one out, and leaving out wins.
22 assert.ok(harvestable("docs/PERFORMANCE.md", "doc", { docs: ["docs/PERFORMANCE.md"], skip: [] }));
23 assert.ok(harvestable("docs/notes/x.md", "doc", { docs: ["docs/notes/*"], skip: [] }));
24 assert.ok(!harvestable("README.md", "readme", { docs: [], skip: ["README.md"] }));
25 assert.ok(!harvestable("docs/testing.md", "doc", { docs: ["docs/testing.md"], skip: ["docs/*"] }));
26});
27
28const FEEDBACK = `# Building a platform: a field report
29
30### A1. What is a billable operation? (blocking)
31
32- **What we tried.** Price from cost: pass storage through to each workspace with a modest
33 uniform overhead, so a bill tracks what it really costs us.
34- **What we hit.** Pricing defines operations only loosely.
35- **What it means.** Our capacity model gives a 15x spread.
36- **Ask.** A table: each binding method and each endpoint, billable or not.
37
38### Conventions we would like
39
40- **Ask.** Metrics with the same event names as the invoice, per repository.
41`;
42
43const PLAN = `# Plan
44
45## For people who do not write code
46
47- **Roles.** A person can plan and approve work without ever cloning a
48 repo. The roles that were sketched here map onto the five repository
49 roles that are built.
50
51### Rules every automation obeys
52
53- **Deduplication.** The same Sentry issue firing 500 times maps to one
54 issue.
55- **Loop protection.** Work started by an automation cannot retrigger the
56 same automation without a person in between.
57
58## Roadmap
59
60## Later
61
62- g1t will learn from docs.
63`;
64
65test("a doc that reads as feedback or a plan says nothing, whatever its name", () => {
66 assert.ok(planLike(FEEDBACK));
67 assert.deepEqual(extract("CONTRIBUTING.md", FEEDBACK, ctx).hints, []);
68 assert.ok(planLike(PLAN));
69 assert.deepEqual(extract("docs/testing.md", PLAN, ctx).hints, []);
70 const files = [
71 { path: "docs/PLAN.md", facts: extract("docs/PLAN.md", "# Working on g1t\n\n## Conventions\n\n- Run the whole suite before you push, every time.\n", ctx) },
72 { path: "docs/CLOUDFLARE_FEEDBACK.md", facts: extract("docs/CLOUDFLARE_FEEDBACK.md", FEEDBACK, ctx) },
73 ];
74 const project = { id: "p", workspace: "w", slug: "g1t", name: "g1t", description: null, private: false, repoId: "r", repo: { namespace: "w", name: "g1t" }, rootDir: "", defaultBranch: "main" };
75 const around = { owners: [], deploy: null, integrations: [] };
76 assert.deepEqual(assemble(project, files, around).hints, [], "docs/PLAN.md is not a source of memory");
77 // Unless project.yml says it is.
78 const withConfig = [...files, { path: ".g1t/project.yml", facts: extract(".g1t/project.yml", "memory:\n docs:\n - docs/PLAN.md\n", ctx) }];
79 assert.deepEqual(assemble(project, withConfig, around).hints.map((hint) => hint.path), ["docs/PLAN.md"]);
80});
81
82test("a bullet is taken whole, with the lines it wraps onto", () => {
83 const bullets = bulletsOf(
84 "## Conventions\n\n- **Loop protection.** Work started by an automation cannot retrigger the\n same automation without a person in between.\n- Short one.\n\nA paragraph.\n\n```\n- not a bullet\n```\n",
85 );
86 assert.deepEqual(bullets, [
87 { text: "**Loop protection.** Work started by an automation cannot retrigger the same automation without a person in between.", heading: "Conventions" },
88 { text: "Short one.", heading: "Conventions" },
89 ]);
90 // Bullets in a plan section, and its subsections, are left out.
91 assert.deepEqual(bulletsOf("## Roadmap\n\n### Next\n\n- One.\n\n## Testing\n\n- Two.\n").map((b) => b.text), ["Two."]);
92});
93
94test("never cut inside a sentence: whole sentences up to the limit, or nothing", () => {
95 assert.deepEqual(sentencesOf("Run `npm test` first. It reads README.md, e.g. the setup. Done!"), [
96 "Run `npm test` first.",
97 "It reads README.md, e.g. the setup.",
98 "Done!",
99 ]);
100 assert.equal(wholeSentences("Short and whole", 40), "Short and whole");
101 assert.equal(wholeSentences("First sentence is here. Second sentence pushes it past the limit.", 30), "First sentence is here.");
102 assert.equal(wholeSentences("One very long sentence that never ends before the limit is reached at all", 30), null);
103});
104
105test("plans and wishes are not memory; kinds are earned, not assumed", () => {
106 assert.ok(aspirational("g1t will learn from docs.", "doc"));
107 assert.ok(aspirational("Tests should run on every push.", "contributing"));
108 assert.ok(aspirational("**Ask.** A usage endpoint.", "doc"));
109 assert.ok(aspirational("**What we tried.** Pass storage through.", "doc"));
110 assert.ok(!aspirational("Run `cargo test` in the crate you changed.", "contributing"));
111 // In AGENTS.md "should" states a rule.
112 assert.ok(!aspirational("You should run the formatter before committing.", "agents"));
113 assert.ok(aspirational("TODO: document the release flow.", "agents"));
114
115 assert.deepEqual(classify("Never edit generated files by hand.", "Conventions", "contributing"), { kind: "gotcha", confidence: 0.6 });
116 assert.deepEqual(classify("Use the shared client for every call.", "Conventions", "contributing"), { kind: "convention", confidence: 0.6 });
117 assert.deepEqual(classify("The API lives in apps/api and serves api.g1t.sh.", "Conventions", "readme"), { kind: "fact", confidence: 0.5 });
118});
119
120test("CONTRIBUTING's before-you-push bullets are suggested whole; nothing is cut", () => {
121 const facts = extract(
122 "CONTRIBUTING.md",
123 "# Contributing\n\n## Before you push\n\n- `cargo test` in the crate or service you changed.\n- `npx tsc -b --force` in `apps/web` (the incremental build misses changes\n in `packages/contracts`).\n- Look at what you changed in a browser. Screenshots catch what type\n checks do not.\n\n## Deploying\n\nPushes to `main` deploy themselves.\n",
124 ctx,
125 );
126 assert.deepEqual(
127 facts.hints.map((hint) => [hint.kind, hint.text, hint.confidence]),
128 [
129 ["fact", "`cargo test` in the crate or service you changed.", 0.5],
130 ["fact", "`npx tsc -b --force` in `apps/web` (the incremental build misses changes in `packages/contracts`).", 0.5],
131 ["convention", "Look at what you changed in a browser. Screenshots catch what type checks do not.", 0.6],
132 ],
133 );
134 for (const hint of facts.hints) assert.match(hint.text, /[.!?)`]$/);
135});