cursedbelt 2.7.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,181 @@
1
+ /**
2
+ * 🔴 The gate got faster on 2026-09-15. This is the file that stops it getting smaller.
3
+ *
4
+ * `bun run verify` was `paths && typecheck && demo:check && build && test && e2e` — six
5
+ * serial links, 142.0s measured end to end on an idle machine, five of them single-threaded
6
+ * work queued behind each other on a fourteen-core Mac. It is now a dependency graph
7
+ * (`scripts/verify.ts`) that runs the same stages concurrently.
8
+ *
9
+ * Every speed-up of a gate is one edit away from being a deletion of a gate, and the two
10
+ * look identical in a diff: dropping `e2e` from the graph makes `verify` four times faster
11
+ * and every test still passes. This generation's third rule is that a repo's gate proves
12
+ * that repo, so the SET of stages is pinned here, by name, against the chain that existed
13
+ * before the change. Adding a stage is free; removing one has to argue with this file.
14
+ *
15
+ * The literal list below is deliberate duplication. A test that derived the expectation from
16
+ * `STAGES` would assert that the graph equals itself.
17
+ */
18
+ import { describe, expect, it } from 'bun:test';
19
+ import { readFileSync, readdirSync, statSync } from 'node:fs';
20
+ import { join, resolve } from 'node:path';
21
+ import pkg from '../package.json';
22
+ import { graphFault, jobLimit, ROOT, STAGES, timingTable } from '../scripts/verify';
23
+
24
+ /**
25
+ * What `verify` ran before it became a graph, expanded to the leaf scripts.
26
+ *
27
+ * `typecheck` was one script running three `tsc` projects serially; it is now three scripts
28
+ * so they can run at once, and `typecheck` still runs all three for anybody typing it by
29
+ * hand. `styles` is new — it is the first two steps of `build`, hoisted so that the things
30
+ * which READ the generated stylesheets can wait on the thing that writes them.
31
+ */
32
+ const MUST_RUN = [
33
+ 'paths',
34
+ 'typecheck:root',
35
+ 'typecheck:server',
36
+ 'typecheck:specs',
37
+ 'demo:check',
38
+ 'styles',
39
+ 'build',
40
+ 'test',
41
+ 'e2e',
42
+ ] as const;
43
+
44
+ describe('the verify graph', () => {
45
+ it('still runs every check the serial chain ran', () => {
46
+ const inGraph = STAGES.map((s) => s.script);
47
+ const missing = MUST_RUN.filter((script) => !inGraph.includes(script));
48
+ expect(
49
+ missing,
50
+ `\`bun run verify\` no longer runs:\n ${missing.join('\n ')}\n` +
51
+ 'A gate that got faster by proving less is not faster. Add the stage back, or — if it ' +
52
+ 'genuinely belongs somewhere else now — say so here and in scripts/verify.ts together.',
53
+ ).toEqual([]);
54
+ });
55
+
56
+ it('names only scripts package.json actually declares', () => {
57
+ const scripts = pkg.scripts as Record<string, string | undefined>;
58
+ const undeclared = STAGES.map((s) => s.script).filter((script) => !scripts[script]);
59
+ expect(undeclared, `verify stages with no script:\n ${undeclared.join('\n ')}`).toEqual([]);
60
+ });
61
+
62
+ it('is the thing `bun run verify` actually runs', () => {
63
+ // Without this the graph can be perfect and unreachable: `verify` could quietly go
64
+ // back to a chain, and every assertion above would still pass.
65
+ expect(pkg.scripts.verify).toInclude('scripts/verify.ts');
66
+ });
67
+
68
+ it('has no dangling `needs` and no cycle', () => {
69
+ expect(graphFault(STAGES)).toBeNull();
70
+ });
71
+
72
+ it('catches a dangling `needs` rather than idling with work left', () => {
73
+ // The negative control. A typo in `needs` makes a stage that can never become ready,
74
+ // and a scheduler that simply runs out of ready work would exit 0 having skipped it —
75
+ // "nothing failed" reported as "everything passed", which is the false green in its
76
+ // purest form.
77
+ expect(graphFault([{ script: 'a' }, { script: 'b', needs: ['typo'] }])).toInclude('typo');
78
+ });
79
+
80
+ it('catches a cycle', () => {
81
+ expect(graphFault([{ script: 'a', needs: ['b'] }, { script: 'b', needs: ['a'] }])).toInclude('cycle');
82
+ });
83
+
84
+ it('catches two stages claiming the same script', () => {
85
+ expect(graphFault([{ script: 'a' }, { script: 'a' }])).toInclude('same script');
86
+ });
87
+ });
88
+
89
+ describe('the two edges the graph rests on', () => {
90
+ it('keeps everything that reads the generated stylesheets behind `styles`', () => {
91
+ // `build` regenerates src/styles-static.css and src/styles-utilities.css;
92
+ // stylesStaticMatches.spec.ts and stylesUtilitiesMatches.spec.ts assert those exact
93
+ // files are current, and the demo bundle e2e drives is compiled from them. Any of
94
+ // those three running beside the generators is a writer racing a reader.
95
+ for (const script of ['build', 'test', 'e2e']) {
96
+ const stage = STAGES.find((s) => s.script === script);
97
+ expect(stage?.needs, `\`${script}\` must wait for \`styles\``).toContain('styles');
98
+ }
99
+ });
100
+
101
+ it('🔴 no spec reads dist/, which is why `build` is a leaf', () => {
102
+ // The graph runs `build` BESIDE `test` and `e2e` rather than in front of them, and
103
+ // that is only sound while nothing under test can observe the compiled output. The
104
+ // build stages into dist.next and swaps, so a reader of `dist/` during a build sees
105
+ // the previous bytes or — for one `mv` — no directory at all.
106
+ const offenders: string[] = [];
107
+ const walk = (dir: string): void => {
108
+ for (const entry of readdirSync(dir)) {
109
+ const full = join(dir, entry);
110
+ if (statSync(full).isDirectory()) {
111
+ walk(full);
112
+ continue;
113
+ }
114
+ if (!/\.spec\.tsx?$/.test(entry)) continue;
115
+ // This file states the pattern, so it matches itself — the same self-exemption
116
+ // `namedSubpathsResolve.spec.ts` needs for the same reason.
117
+ if (entry === 'verifyGraph.spec.ts') continue;
118
+ const text = readFileSync(full, 'utf8');
119
+ // A filesystem read whose path argument mentions dist — not the word in prose,
120
+ // and not `dist.next` inside buildIsStaged's assertions about the SCRIPT TEXT.
121
+ if (/(?:readFileSync|readdirSync|existsSync|statSync|Bun\.file|new Glob)\([^)]*['"`][^'"`]*\bdist\b/.test(text)) {
122
+ offenders.push(full.slice(ROOT.length + 1));
123
+ }
124
+ }
125
+ };
126
+ walk(resolve(ROOT, 'src'));
127
+ expect(
128
+ offenders,
129
+ `these specs read dist/, so \`build\` is no longer a leaf and the graph is wrong:\n ${offenders.join('\n ')}\n` +
130
+ 'Either give them `needs: ["build"]` in scripts/verify.ts, or stop reading dist.',
131
+ ).toEqual([]);
132
+ });
133
+ });
134
+
135
+ describe('the job limit', () => {
136
+ it('defaults to a third of the cores, because a stage is not a process', () => {
137
+ // e2e alone spawns seven Playwright workers; build spawns two tsc processes. Six
138
+ // stages is already twenty-odd processes on a machine with fourteen cores.
139
+ expect(jobLimit({}, 14)).toBe(4);
140
+ expect(jobLimit({}, 4)).toBe(2);
141
+ expect(jobLimit({}, 64)).toBe(6);
142
+ });
143
+
144
+ it('honours an explicit override, including 1', () => {
145
+ // 🔴 `1` is the old serial behaviour, and it must stay reachable without a second
146
+ // gate script existing for somebody to run instead of this one.
147
+ expect(jobLimit({ CURSEDBELT_VERIFY_JOBS: '1' }, 14)).toBe(1);
148
+ expect(jobLimit({ CURSEDBELT_VERIFY_JOBS: '9' }, 14)).toBe(9);
149
+ });
150
+
151
+ it('ignores nonsense rather than running zero stages at a time', () => {
152
+ expect(jobLimit({ CURSEDBELT_VERIFY_JOBS: 'lots' }, 14)).toBe(4);
153
+ expect(jobLimit({ CURSEDBELT_VERIFY_JOBS: '0' }, 14)).toBe(4);
154
+ expect(jobLimit({ CURSEDBELT_VERIFY_JOBS: '-3' }, 14)).toBe(4);
155
+ });
156
+ });
157
+
158
+ describe('the timing table', () => {
159
+ it('reports the overlap, which is the whole point of the change', () => {
160
+ const table = timingTable(
161
+ [
162
+ { script: 'test', ok: true, ms: 60_000, output: '' },
163
+ { script: 'e2e', ok: true, ms: 30_000, output: '' },
164
+ ],
165
+ 60_000,
166
+ );
167
+ expect(table).toInclude('1.5× overlap');
168
+ expect(table).toInclude('test');
169
+ });
170
+
171
+ it('says so plainly when nothing overlapped', () => {
172
+ const table = timingTable([{ script: 'paths', ok: true, ms: 1_000, output: '' }], 1_000);
173
+ expect(table).toInclude('No overlap');
174
+ });
175
+
176
+ it('shows a skipped stage as skipped, never as passing', () => {
177
+ const table = timingTable([{ script: 'e2e', ok: false, ms: 0, output: '', skipped: true }], 1_000);
178
+ expect(table).toInclude('skipped');
179
+ expect(table).not.toInclude(' ok');
180
+ });
181
+ });