ReviewOS

also looking at this

craft-native/craft

Brief agents on the environment a fresh checkout cannot infer

#319
Merged glennmichael123 wants to merge fix/agents-environment-briefing into main
4 files +126 -0
scripts/agents-guidelines.tsadded+45-0
Changes to scripts/agents-guidelines.ts
@@ -0,0 +1,45 @@
1/**
2 * Keeps `AGENTS.md` a real briefing rather than a copy of `CLAUDE.md`.
3 *
4 * `AGENTS.md` is the only thing a coding agent working from a fresh checkout
5 * reads before it runs a command. It was byte-identical to `CLAUDE.md` below
6 * its title, so it carried the house rules and none of the environment facts
7 * that a machine without this repository's history cannot discover.
8 *
9 * Every topic below cost a real, separately diagnosed build failure here, in
10 * this order: an unpinned `zig` on PATH, the absent sibling checkouts, and a
11 * `bun test` that ran the wrong suite. None of them are code defects and none
12 * are visible from the tree, which is exactly why they belong in writing.
13 */
14
15/** Facts an agent cannot derive from the tree, each with the words that prove it was written down. */
16const briefings: { topic: string, needles: string[] }[] = [
17 { topic: 'the pinned Zig toolchain', needles: ['CRAFT_ZIG', 'pantry.lock'] },
18 { topic: 'the sibling first-party checkouts', needles: ['Libraries/zig-js', 'pins.env'] },
19 { topic: 'which suite each test script runs', needles: ['bun run test:sdk'] },
20]
21
22/** House rules that must survive any rewrite, since the copy is where they came from. */
23const houseRules = ['pickier', 'buddy-bot', 'conventional commit']
24
25/** The text below the first heading, which is the only line the two files are meant to differ on. */
26function body(text: string): string {
27 return text.split('\n').slice(1).join('\n').trim()
28}
29
30export function checkAgentsGuidelines(agents: string, claude: string): void {
31 if (body(agents) === body(claude))
32 throw new Error('AGENTS.md repeats CLAUDE.md below its title, so it briefs none of the environment a fresh checkout needs')
33
34 for (const { topic, needles } of briefings) {
35 const missing = needles.filter(needle => !agents.includes(needle))
36 if (missing.length)
37 throw new Error(`AGENTS.md does not brief ${topic}; expected to find ${missing.join(' and ')}`)
38 }
39
40 const lowered = agents.toLowerCase()
41 for (const rule of houseRules) {
42 if (!lowered.includes(rule))
43 throw new Error(`AGENTS.md dropped the shared house rule: ${rule}`)
44 }
45}