-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathgate-data.ts
More file actions
262 lines (256 loc) · 8.68 KB
/
Copy pathgate-data.ts
File metadata and controls
262 lines (256 loc) · 8.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
// Static data for the gate: the containment denylists and the check registry. Split from gate.ts
// so the engine (git/spawn/containment logic) and its data each stay small and reviewable.
// A staged file is forbidden if a parent dir is here, or its exact path is in the file set.
export const FORBIDDEN_DIRS = new Set([
"harness",
"frontend/harness",
".githooks",
".github",
]);
// Forbidden by basename anywhere in the tree (**/name): a package.json in any package can
// declare dependencies/scripts/size budgets the gate trusts, so none may be committed by an agent.
export const FORBIDDEN_BASENAMES = new Set(["package.json"]);
export const FORBIDDEN_FILES = new Set([
"AGENTS.md",
"PROMPT.md",
"docs/plan.md",
"harness/preferences.ts",
// tooling/config that would weaken the gate's thresholds or its checks
"package.json",
"pnpm-lock.yaml",
"pnpm-workspace.yaml",
"frontend/package.json",
"frontend/pnpm-lock.yaml",
"frontend/tsconfig.json",
"harness/package.json",
"harness/pnpm-lock.yaml",
"harness/tsconfig.json",
"harness/tsconfig.app.json",
"tsconfig.cruise.json",
"harness/tsconfig.harness.json",
"harness/vitest.config.js",
"harness/eslint.config.js",
"harness/stylelint.config.js",
"harness/knip.json",
"harness/cspell.json",
"harness/.prettierrc.json",
"harness/biome.json",
"harness/.prettierignore",
"harness/.secretlintrc.json",
"harness/.spectral.yml",
"harness/.dependency-cruiser.cjs",
"harness/playwright.config.js",
"harness/lighthouserc.cjs",
"harness/.htmlvalidate.json",
// html-validate only discovers ignore files from the scan root, so this one lives there.
".htmlvalidateignore",
]);
// Escape hatches addable in normal source; config-file hatches are moot (configs are forbidden).
export const FORBIDDEN_PATTERNS = [
"eslint-disable",
"stylelint-disable",
"html-validate-disable",
"biome-ignore",
"prettier-ignore",
"ts-ignore",
"ts-nocheck",
"ts-expect-error",
"v8 ignore",
"c8 ignore",
"istanbul ignore",
"vitest ignore",
"semgrep:ignore",
"nosec",
"secretlint-disable",
"cspell:disable",
"cspell:ignore",
"--no-verify",
"hooksPath",
".only(",
"test.only",
"it.only",
"describe.only",
"test.skip",
"it.skip",
"describe.skip",
"depcruise: ignore",
"knipignore",
"skipLibCheck",
"coverage=false",
"lighthouse:skip",
] as const;
// Harness-owned tools are invoked by BARE NAME. The gate runs checks with harness/node_modules/
// .bin prepended to PATH (see checkEnvironment in gate.ts), so `eslint`/`prettier`/… resolve
// wherever pnpm links them — no hard-coded path, robust across install layouts.
const tool = (name: string): string => name;
// Fast checks every committer pays. Each check names its harness-owned config explicitly: the
// configs live in harness/ (protected as FORBIDDEN_FILES) and are NOT ancestors of the linted
// files, so the tools' own cosmiconfig walk-up would never find them — the --config flag is what
// makes the protected config actually govern the check.
export const COMMIT_CHECKS: Record<string, string[]> = {
format: [
tool("prettier"),
// Scope to the two source packages — app code and harness tooling — so the
// glob never walks node_modules (~19k dirs) and hangs past the preflight
// timeout, while still formatting harness/. The --ignore-path is defense in
// depth (node_modules, dist, scratchpad…); the rooted targets bound the walk.
"frontend/",
"harness/",
"--check",
"--ignore-path",
"harness/.prettierignore",
"--cache",
"--cache-location",
".cache_prettier",
],
eslint: [
tool("eslint"),
".",
"--config",
"harness/eslint.config.js",
"--cache",
"--cache-location",
".",
"--max-warnings=0",
],
style: [
tool("stylelint"),
// Scope to frontend so the glob never descends into node_modules (~19k dirs) and
// hangs past the preflight timeout. stylelint's ignore lists (ignoreFiles /
// --ignore-pattern / --ignore-path) filter AFTER glob expansion, so they cannot
// prevent the walk — only a frontend-rooted glob can. All project CSS lives under
// frontend/. The --ignore-path is defense in depth (node_modules, dist, scratchpad…).
"frontend/**/*.css",
"--config",
"harness/stylelint.config.js",
"--ignore-path",
"harness/.stylelintignore",
"--max-warnings=0",
"--allow-empty-input",
],
html: [
tool("html-validate"),
"--config",
"harness/.htmlvalidate.json",
"**/*.html",
],
};
// The full bar: app, harness tooling, dependency/security, and browser checks.
//
// FOLLOW-UP (size budget): a per-package `size` check (size-limit) belongs here. The previous size
// subsystem was removed during the library/template redesign; without it an agent can sneak
// new/oversized files into a bundle without tripping a budget. Re-adding it means a `size` entry
// here plus its tests (removed from gate.test.ts — recover the shape from Git history at a721b9a).
export const FULL_CHECKS: Record<string, string[]> = {
...COMMIT_CHECKS,
// The forbidden app tsconfig governs the typecheck (browser types, strict flags); pinned
// with -p so an agent can't repoint it at a weaker project tsconfig. It lives in harness/
// so tsc and typescript-eslint resolve `vite/client` from harness/node_modules (Vite is not
// installed at the repo root). dependency-cruiser cannot use it directly — it resolves the
// tsconfig include from its repo-root cwd, so a harness/-relative ../frontend include trips
// TS18003 — so cruise gets its own root-level tsconfig.cruise.json (extends this) instead.
typecheck: [
tool("tsc"),
"-p",
"harness/tsconfig.app.json",
"--noEmit",
"--incremental",
"--tsBuildInfoFile",
".cache_tsbuildinfo_app",
],
harnessTypes: [
tool("tsc"),
"-p",
"harness/tsconfig.harness.json",
"--noEmit",
"--incremental",
"--tsBuildInfoFile",
".cache_tsbuildinfo_harness",
],
schema: [
tool("ajv"),
"compile",
"-s",
"frontend/schemas/**/*.schema.json",
"--spec=draft2020",
"--strict=true",
"--all-errors",
"-c",
"ajv-formats",
"-c",
"ajv-keywords",
],
cruise: [
tool("depcruise"),
"frontend/src",
"--config",
"harness/.dependency-cruiser.cjs",
"--output-type",
"err",
],
deadcode: [tool("knip"), "--config", "harness/knip.json"],
// Spelling is advisory: --no-exit-code makes cspell print issues but always exit 0, so a typo
// (or a domain word not yet in the allowlist) warns loudly without ever failing the gate.
spelling: [
tool("cspell"),
".",
"--config",
"harness/cspell.json",
"--no-progress",
"--no-summary",
"--no-exit-code",
],
workflow: [
tool("spectral"),
"lint",
".github/workflows/ci.yml",
"--ruleset",
"harness/.spectral.yml",
"--fail-severity=warn",
],
sast: [
"semgrep",
"scan",
"--config=p/typescript",
"--config=p/javascript",
"--config=p/security-audit",
"--error",
"--metrics=off",
],
secrets: [
tool("secretlint"),
"**/*",
"--secretlintrc",
"harness/.secretlintrc.json",
],
// pnpm audit reads the pnpm-lock.yaml; --dir targets the frontend package. Covers the same
// dependency-vulnerability surface npm audit did before the pnpm migration.
audit: ["pnpm", "--dir", "frontend", "audit", "--audit-level", "high"],
// No `lockfile` (lockfile-lint) check: lockfile-lint has no pnpm-lock parser (npm/yarn only),
// and pnpm already blocks the injection vector it guarded — pnpm refuses to install packages in
// pnpm-lock.yaml that are absent from package.json, and the lock does not carry alternative HTTP
// tarball sources for registry packages. No `npmSignatures`: `pnpm audit signatures` needs an
// installed-package context the gate can't give cleanly; osv + pnpm audit cover dependency risk.
// No `versions` (syncpack) check: this is a TEMPLATE — packages intentionally track `latest`, so
// enforcing cross-package version consistency fights the intent. Downstream repos may re-add it.
// Disabled: this is a template — deps track `latest`, so transitive-dep CVEs
// are noise here. Downstream repos that pin versions should re-enable.
// osv: [
// "osv-scanner",
// "scan",
// "source",
// "--lockfile=frontend/pnpm-lock.yaml",
// "--lockfile=harness/pnpm-lock.yaml",
// ],
build: ["pnpm", "--dir", "frontend", "run", "build"],
coverage: [
tool("vitest"),
"run",
"--config",
"harness/vitest.config.js",
"--cache",
"--coverage",
],
e2e: [tool("playwright"), "test", "--config", "harness/playwright.config.js"],
lighthouse: [tool("lhci"), "autorun", "--config", "harness/lighthouserc.cjs"],
};