@orkestrel/scaffold 0.0.63 → 0.0.65
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.
- package/README.md +29 -104
- package/dist/bin/main.js +95 -27
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +2 -2
- package/dist/host/CLAUDE.md +6 -0
- package/dist/host/agents/orchestration.md +23 -15
- package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +184 -177
- package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +314 -91
- package/dist/host/agents/skills/enterprise-bootstrap/references/color-modes.md +241 -0
- package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +83 -36
- package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +297 -98
- package/dist/host/agents/skills/enterprise-bootstrap/references/inputs.md +25 -14
- package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +216 -128
- package/dist/host/agents/skills/enterprise-bootstrap/references/responsive-layout.md +187 -0
- package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +109 -20
- package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +5 -5
- package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +15 -15
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +43 -17
- package/dist/host/agents/skills/orkestrel-publish/references/window.md +41 -16
- package/dist/host/claude/agents/orkestrel.md +56 -56
- package/dist/host/claude/agents/reviewer.md +13 -0
- package/dist/host/claude/rules/architecture.md +51 -45
- package/dist/host/claude/rules/documentation.md +18 -1
- package/dist/host/claude/rules/portability.md +2 -0
- package/dist/host/claude/rules/quality.md +1 -1
- package/dist/host/claude/rules/tests.md +12 -11
- package/dist/host/claude/rules/typescript.md +5 -0
- package/dist/host/claude/rules/workspace.md +25 -20
- package/dist/host/claude/rules/writing.md +4 -0
- package/dist/host/claude/settings.json +1 -1
- package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +10 -9
- package/dist/host/codex/agents/orkestrel.toml +3 -3
- package/dist/host/codex/agents/reviewer.toml +4 -2
- package/dist/host/configs/helpers.ts +311 -2
- package/dist/host/configs/policy.ts +1100 -51
- package/dist/host/dotfiles/oxlintrc.json +72 -1
- package/dist/host/guides/guide.md +749 -222
- package/dist/host/guides/scaffold.md +529 -394
- package/dist/host/manifest.json +53 -40
- package/dist/host/scripts/ollama.sh +322 -13
- package/dist/host/tests/config.test.ts +1200 -16
- package/dist/host/tests/policy.test.ts +157 -173
- package/dist/host/tests/setupPolicy.ts +522 -1007
- package/dist/src/core/index.cjs +402 -287
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +160 -128
- package/dist/src/core/index.d.ts +160 -128
- package/dist/src/core/index.js +400 -286
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +28 -21
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +38 -33
- package/dist/src/server/index.d.ts +38 -33
- package/dist/src/server/index.js +28 -21
- package/dist/src/server/index.js.map +1 -1
- package/package.json +18 -19
|
@@ -1,29 +1,40 @@
|
|
|
1
|
-
import { existsSync, readdirSync } from 'node:fs'
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import { describe, expect, it } from 'vitest'
|
|
4
|
+
import { POLICY_BANNED_TERMS, POLICY_JUDGED_TERMS } from '../configs/policy.js'
|
|
4
5
|
import {
|
|
5
6
|
BRIDGE_POLICY_CONTROLS,
|
|
6
7
|
createPolicyScratch,
|
|
7
8
|
createSkillMetadata,
|
|
8
|
-
FUNCTION_SOURCE_FILES,
|
|
9
|
-
GENERIC_POLICY_SOURCES,
|
|
10
9
|
inspectPolicyControl,
|
|
11
10
|
inspectPolicyFilenamePaths,
|
|
12
11
|
inspectPolicyMirrorPaths,
|
|
13
12
|
inspectPolicyPortability,
|
|
14
|
-
|
|
13
|
+
inspectPolicyWiring,
|
|
15
14
|
inspectPolicyWorkspace,
|
|
16
15
|
inspectSkillFamily,
|
|
17
16
|
inspectSkillBridges,
|
|
17
|
+
isPolicyFile,
|
|
18
|
+
isPolicyMirror,
|
|
19
|
+
isPolicyRecord,
|
|
20
|
+
isPolicyStray,
|
|
18
21
|
matchesSkillTrigger,
|
|
19
22
|
parseSkillFrontmatter,
|
|
23
|
+
POLICY_CATALOG_FILE,
|
|
20
24
|
POLICY_CONTROLS,
|
|
25
|
+
POLICY_MIRROR_PATTERN,
|
|
21
26
|
POLICY_SUPPRESSION_DIRECTIVE,
|
|
27
|
+
POLICY_TERM_FILE,
|
|
28
|
+
POLICY_TERM_HEADING,
|
|
22
29
|
PORTABILITY_POLICY_CONTROLS,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
PROSE_POLICY_CONTROLS,
|
|
31
|
+
PROSE_POLICY_MANIFEST,
|
|
32
|
+
readPolicyCatalog,
|
|
33
|
+
readPolicyGuide,
|
|
34
|
+
readPolicyPackage,
|
|
26
35
|
readPolicyPaths,
|
|
36
|
+
readPolicyProse,
|
|
37
|
+
readPolicyTerms,
|
|
27
38
|
readSkillFamily,
|
|
28
39
|
RULES_POLICY_CONTROLS,
|
|
29
40
|
SKILL_BRIDGE_ROOT,
|
|
@@ -55,15 +66,6 @@ describe('policy scratch', () => {
|
|
|
55
66
|
})
|
|
56
67
|
|
|
57
68
|
describe('fleet policy register', () => {
|
|
58
|
-
it('keeps handlers in the function set and routes out', () => {
|
|
59
|
-
expect(FUNCTION_SOURCE_FILES).toContain('handlers.ts')
|
|
60
|
-
expect(FUNCTION_SOURCE_FILES).not.toContain('routes.ts')
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('accepts a differently shaped workspace without a core environment', () => {
|
|
64
|
-
expect(inspectPolicySources(GENERIC_POLICY_SOURCES)).toEqual([])
|
|
65
|
-
})
|
|
66
|
-
|
|
67
69
|
it('accepts matching mirrors across arbitrary axes and environments', () => {
|
|
68
70
|
const tests = [
|
|
69
71
|
'tests/src/worker/Worker.test.ts',
|
|
@@ -178,152 +180,6 @@ describe('policy population controls', () => {
|
|
|
178
180
|
).toEqual([])
|
|
179
181
|
})
|
|
180
182
|
|
|
181
|
-
it('excludes a .d.ts ambient declaration from placement', () => {
|
|
182
|
-
expect(
|
|
183
|
-
inspectPolicyControl({
|
|
184
|
-
label: 'excludes a .d.ts ambient declaration from placement',
|
|
185
|
-
membership: 'ambient TypeScript declarations',
|
|
186
|
-
rule: 'type',
|
|
187
|
-
files: [
|
|
188
|
-
{
|
|
189
|
-
path: 'app/browser/env.d.ts',
|
|
190
|
-
content: 'export interface EnvironmentInterface {}\n',
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
}),
|
|
194
|
-
).toEqual([])
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
it('excludes a .d.mts ambient declaration from placement', () => {
|
|
198
|
-
expect(
|
|
199
|
-
inspectPolicyControl({
|
|
200
|
-
label: 'excludes a .d.mts ambient declaration from placement',
|
|
201
|
-
membership: 'ambient TypeScript declarations',
|
|
202
|
-
rule: 'type',
|
|
203
|
-
files: [
|
|
204
|
-
{
|
|
205
|
-
path: 'app/browser/env.d.mts',
|
|
206
|
-
content: 'export interface EnvironmentInterface {}\n',
|
|
207
|
-
},
|
|
208
|
-
],
|
|
209
|
-
}),
|
|
210
|
-
).toEqual([])
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
it('excludes a .d.cts ambient declaration from placement', () => {
|
|
214
|
-
expect(
|
|
215
|
-
inspectPolicyControl({
|
|
216
|
-
label: 'excludes a .d.cts ambient declaration from placement',
|
|
217
|
-
membership: 'ambient TypeScript declarations',
|
|
218
|
-
rule: 'type',
|
|
219
|
-
files: [
|
|
220
|
-
{
|
|
221
|
-
path: 'app/browser/env.d.cts',
|
|
222
|
-
content: 'export interface EnvironmentInterface {}\n',
|
|
223
|
-
},
|
|
224
|
-
],
|
|
225
|
-
}),
|
|
226
|
-
).toEqual([])
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
it('accepts a direct callback argument in constants.ts', () => {
|
|
230
|
-
expect(
|
|
231
|
-
inspectPolicyControl({
|
|
232
|
-
label: 'accepts a direct callback argument in constants.ts',
|
|
233
|
-
membership: 'anonymous callbacks passed directly as call arguments',
|
|
234
|
-
rule: 'function',
|
|
235
|
-
files: [
|
|
236
|
-
{
|
|
237
|
-
path: 'app/edge/constants.ts',
|
|
238
|
-
content: 'export const LABELS = Object.freeze(COLUMNS.map((column) => column.label))\n',
|
|
239
|
-
},
|
|
240
|
-
],
|
|
241
|
-
}),
|
|
242
|
-
).toEqual([])
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
it('accepts a helper namespace in helpers.ts', () => {
|
|
246
|
-
expect(
|
|
247
|
-
inspectPolicyControl({
|
|
248
|
-
label: 'accepts a helper namespace in helpers.ts',
|
|
249
|
-
membership: 'camelCase helper namespaces containing function behavior',
|
|
250
|
-
rule: 'data',
|
|
251
|
-
files: [
|
|
252
|
-
{
|
|
253
|
-
path: 'app/edge/helpers.ts',
|
|
254
|
-
content: 'export const formatters = Object.freeze({ money: build(fmt) })\n',
|
|
255
|
-
},
|
|
256
|
-
],
|
|
257
|
-
}),
|
|
258
|
-
).toEqual([])
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
it('accepts a concise-body direct return in constants.ts', () => {
|
|
262
|
-
expect(
|
|
263
|
-
inspectPolicyControl({
|
|
264
|
-
label: 'accepts a concise-body direct return in constants.ts',
|
|
265
|
-
membership: 'anonymous functions returned directly through a concise arrow body',
|
|
266
|
-
rule: 'function',
|
|
267
|
-
files: [
|
|
268
|
-
{
|
|
269
|
-
path: 'app/edge/constants.ts',
|
|
270
|
-
content: 'export const WRAPPED = wrap(() => () => 1)\n',
|
|
271
|
-
},
|
|
272
|
-
],
|
|
273
|
-
}),
|
|
274
|
-
).toEqual([])
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
it('accepts a returned function inside a direct callback', () => {
|
|
278
|
-
expect(
|
|
279
|
-
inspectPolicyControl({
|
|
280
|
-
label: 'accepts a returned function inside a direct callback',
|
|
281
|
-
membership: 'anonymous functions returned directly by a return statement',
|
|
282
|
-
rule: 'function',
|
|
283
|
-
files: [
|
|
284
|
-
{
|
|
285
|
-
path: 'app/edge/constants.ts',
|
|
286
|
-
content:
|
|
287
|
-
'export const LABELS = Object.freeze(COLUMNS.map((column) => { return () => column.label }))\n',
|
|
288
|
-
},
|
|
289
|
-
],
|
|
290
|
-
}),
|
|
291
|
-
).toEqual([])
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
it('accepts directly returned functions through callback control flow', () => {
|
|
295
|
-
expect(
|
|
296
|
-
inspectPolicyControl({
|
|
297
|
-
label: 'accepts directly returned functions through callback control flow',
|
|
298
|
-
membership: 'anonymous functions returned directly through control-flow branches',
|
|
299
|
-
rule: 'function',
|
|
300
|
-
files: [
|
|
301
|
-
{
|
|
302
|
-
path: 'app/edge/constants.ts',
|
|
303
|
-
content:
|
|
304
|
-
'export const VALUES = Object.freeze(C.map((c) => { if (c) return () => 1; return () => 2 }))\n',
|
|
305
|
-
},
|
|
306
|
-
],
|
|
307
|
-
}),
|
|
308
|
-
).toEqual([])
|
|
309
|
-
})
|
|
310
|
-
|
|
311
|
-
it('excludes Vue text from the placement population', () => {
|
|
312
|
-
expect(
|
|
313
|
-
inspectPolicyControl({
|
|
314
|
-
label: 'excludes Vue text from the placement population',
|
|
315
|
-
membership: 'files outside the declared TypeScript source extensions',
|
|
316
|
-
rule: 'type',
|
|
317
|
-
files: [
|
|
318
|
-
{
|
|
319
|
-
path: 'app/browser/Panel.vue',
|
|
320
|
-
content: '<script setup lang="ts">\nexport interface PanelInterface {}\n</script>\n',
|
|
321
|
-
},
|
|
322
|
-
],
|
|
323
|
-
}),
|
|
324
|
-
).toEqual([])
|
|
325
|
-
})
|
|
326
|
-
|
|
327
183
|
it('excludes documentation from the suppression population', () => {
|
|
328
184
|
expect(
|
|
329
185
|
inspectPolicyControl({
|
|
@@ -331,6 +187,10 @@ describe('policy population controls', () => {
|
|
|
331
187
|
membership: 'files outside source, test, config, and script code',
|
|
332
188
|
rule: 'suppression',
|
|
333
189
|
files: [
|
|
190
|
+
// The manifest names the package this guide belongs to, so the prose sweep reads it
|
|
191
|
+
// as the workspace's own guide rather than as a top-level guide no evidence accounts
|
|
192
|
+
// for.
|
|
193
|
+
{ path: 'package.json', content: PROSE_POLICY_MANIFEST },
|
|
334
194
|
{
|
|
335
195
|
path: 'guides/sample.md',
|
|
336
196
|
content: `<!-- ${POLICY_SUPPRESSION_DIRECTIVE} -->\n`,
|
|
@@ -467,16 +327,6 @@ describe('portability policy', () => {
|
|
|
467
327
|
})
|
|
468
328
|
}
|
|
469
329
|
|
|
470
|
-
for (const control of [
|
|
471
|
-
PORTABILITY_POLICY_EXCLUSION,
|
|
472
|
-
PORTABILITY_POLICY_LOCAL,
|
|
473
|
-
PORTABILITY_POLICY_SPLIT,
|
|
474
|
-
]) {
|
|
475
|
-
it(`${control.label} [membership: ${control.membership}]`, () => {
|
|
476
|
-
expect(inspectPolicyControl(control)).toEqual([])
|
|
477
|
-
})
|
|
478
|
-
}
|
|
479
|
-
|
|
480
330
|
it('rejects a character Windows refuses inside a path segment', () => {
|
|
481
331
|
// A Windows host refuses to create this name, so the population itself is the control.
|
|
482
332
|
expect(inspectPolicyFilenamePaths(['src/worker/read<write>.ts'])).toEqual([
|
|
@@ -510,8 +360,115 @@ describe('portability policy', () => {
|
|
|
510
360
|
})
|
|
511
361
|
})
|
|
512
362
|
|
|
363
|
+
describe('prose policy', () => {
|
|
364
|
+
for (const control of PROSE_POLICY_CONTROLS) {
|
|
365
|
+
it(`${control.label} [membership: ${control.membership}]`, () => {
|
|
366
|
+
const violations = inspectPolicyControl(control).filter(
|
|
367
|
+
(violation) => violation.rule === 'prose',
|
|
368
|
+
)
|
|
369
|
+
expect(violations).toHaveLength(1)
|
|
370
|
+
expect(violations[0]?.message).toBe(control.message)
|
|
371
|
+
expect(violations[0]?.line).toBe(control.line)
|
|
372
|
+
})
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// A mirror is excluded from the term sweep, so the exclusion needs evidence rather than
|
|
376
|
+
// silence. The catalog table is that evidence, and it is a second mechanism: it is regenerated
|
|
377
|
+
// from the registry and names the fleet, while the guides directory names what this checkout
|
|
378
|
+
// holds. A guide neither side accounts for reports.
|
|
379
|
+
it('accounts for every top-level guide as this package, the index, or a catalog row', () => {
|
|
380
|
+
const root = process.cwd()
|
|
381
|
+
const own = readPolicyPackage(root)
|
|
382
|
+
if (own === undefined) throw new Error('The workspace manifest declares no name')
|
|
383
|
+
const guides = readPolicyProse(root).filter((path) => POLICY_MIRROR_PATTERN.test(path))
|
|
384
|
+
expect(guides.length).toBeGreaterThan(0)
|
|
385
|
+
expect(guides.filter((path) => isPolicyStray(root, path))).toEqual([])
|
|
386
|
+
const other = readPolicyCatalog(root).find((name) => name !== own)
|
|
387
|
+
if (other === undefined) throw new Error('The catalog registers no other package')
|
|
388
|
+
expect(isPolicyMirror(root, `guides/${other}.md`)).toBe(true)
|
|
389
|
+
expect(isPolicyMirror(root, `guides/${own}.md`)).toBe(false)
|
|
390
|
+
expect(isPolicyMirror(root, 'guides/README.md')).toBe(false)
|
|
391
|
+
expect(isPolicyStray(root, `guides/${own}.md`)).toBe(false)
|
|
392
|
+
expect(isPolicyStray(root, 'guides/README.md')).toBe(false)
|
|
393
|
+
// The control: a name no catalog row registers is a stray whatever the directory holds.
|
|
394
|
+
expect(isPolicyStray(root, 'guides/absent.md')).toBe(true)
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
it('reads the guide name a top-level path carries for another package to account for', () => {
|
|
398
|
+
const root = process.cwd()
|
|
399
|
+
const own = readPolicyPackage(root)
|
|
400
|
+
if (own === undefined) throw new Error('The workspace manifest declares no name')
|
|
401
|
+
expect(readPolicyGuide(root, 'guides/other.md')).toBe('other')
|
|
402
|
+
expect(readPolicyGuide(root, `guides/${own}.md`)).toBeUndefined()
|
|
403
|
+
expect(readPolicyGuide(root, 'guides/README.md')).toBeUndefined()
|
|
404
|
+
expect(readPolicyGuide(root, 'guides/nested/other.md')).toBeUndefined()
|
|
405
|
+
expect(readPolicyGuide(root, 'AGENTS.md')).toBeUndefined()
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
it('reads the authored Markdown population and excludes the directories it names', () => {
|
|
409
|
+
const paths = readPolicyProse(process.cwd())
|
|
410
|
+
expect(paths).toContain('README.md')
|
|
411
|
+
expect(paths).toContain('AGENTS.md')
|
|
412
|
+
expect(paths).toContain('guides/README.md')
|
|
413
|
+
expect(paths).toContain(POLICY_CATALOG_FILE)
|
|
414
|
+
expect(paths.some((path) => path.startsWith('node_modules/'))).toBe(false)
|
|
415
|
+
expect(paths.some((path) => path.startsWith('dist/'))).toBe(false)
|
|
416
|
+
expect(paths.some((path) => path.startsWith('tmp/'))).toBe(false)
|
|
417
|
+
expect(paths.some((path) => path.startsWith('.orkestrel/'))).toBe(false)
|
|
418
|
+
})
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
describe('denylist currency', () => {
|
|
422
|
+
// The denylist is a second copy of one column of the substitution table, so it drifts the
|
|
423
|
+
// moment a row is added or removed. Reading the table itself is the mechanism that could
|
|
424
|
+
// disagree with the copy, and every row has to land in exactly one of the two sets: matched
|
|
425
|
+
// unconditionally, or left to a reader because the row has a permitted sense.
|
|
426
|
+
// A target reads `.claude/rules/writing.md` from the installed scaffold copy rather than
|
|
427
|
+
// authoring it (the canon paragraph `src/core/templates.ts` emits into a generated `AGENTS.md`),
|
|
428
|
+
// so the table exists only where the workspace authors it. There the denylist and the table ship
|
|
429
|
+
// from one release; here they can drift, so the comparison runs here.
|
|
430
|
+
it.skipIf(!isPolicyFile(process.cwd(), POLICY_TERM_FILE))(
|
|
431
|
+
'registers every substitution-table term as either matched or judged',
|
|
432
|
+
() => {
|
|
433
|
+
const registered = new Set([
|
|
434
|
+
...POLICY_BANNED_TERMS.map((entry) => entry.term),
|
|
435
|
+
...POLICY_JUDGED_TERMS,
|
|
436
|
+
])
|
|
437
|
+
const table = readPolicyTerms(readFileSync(join(process.cwd(), POLICY_TERM_FILE), 'utf8'))
|
|
438
|
+
expect(table.length > 0).toBe(true)
|
|
439
|
+
expect([...registered].sort()).toEqual([...new Set(table)].sort())
|
|
440
|
+
},
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
it('reports a table row neither set names', () => {
|
|
444
|
+
// The control: a table carrying one row outside the membership, which the reader returns
|
|
445
|
+
// and neither set holds, so the comparison above would fail on it.
|
|
446
|
+
const fixture = [
|
|
447
|
+
'# Fixture',
|
|
448
|
+
'',
|
|
449
|
+
POLICY_TERM_HEADING,
|
|
450
|
+
'',
|
|
451
|
+
'| Term | Replacement |',
|
|
452
|
+
'| ---- | ----------- |',
|
|
453
|
+
'| `should` | `must` |',
|
|
454
|
+
'| `henceforth` (formal) | `after this` |',
|
|
455
|
+
'',
|
|
456
|
+
'## Not adopted',
|
|
457
|
+
'',
|
|
458
|
+
'| `unreached` | Outside the table |',
|
|
459
|
+
].join('\n')
|
|
460
|
+
const table = readPolicyTerms(fixture)
|
|
461
|
+
expect(table).toEqual(['should', 'henceforth'])
|
|
462
|
+
const registered = new Set([
|
|
463
|
+
...POLICY_BANNED_TERMS.map((entry) => entry.term),
|
|
464
|
+
...POLICY_JUDGED_TERMS,
|
|
465
|
+
])
|
|
466
|
+
expect(table.filter((term) => !registered.has(term))).toEqual(['henceforth'])
|
|
467
|
+
})
|
|
468
|
+
})
|
|
469
|
+
|
|
513
470
|
describe('repository policy', () => {
|
|
514
|
-
it('enforces
|
|
471
|
+
it('enforces the mirror, suppression, skill, bridge, and portability laws over the real workspace', () => {
|
|
515
472
|
expect(inspectPolicyWorkspace(process.cwd())).toEqual([])
|
|
516
473
|
})
|
|
517
474
|
|
|
@@ -569,3 +526,30 @@ describe('repository policy', () => {
|
|
|
569
526
|
expect(inspectPolicyPortability(process.cwd())).toEqual([])
|
|
570
527
|
})
|
|
571
528
|
})
|
|
529
|
+
|
|
530
|
+
describe('policy configuration wiring', () => {
|
|
531
|
+
it('reports a rule no top-level or override rules record enables', () => {
|
|
532
|
+
const configuration = { rules: {}, overrides: [{ files: ['*.ts'], rules: {} }] }
|
|
533
|
+
expect(inspectPolicyWiring(configuration, ['policy/no-mocking'], [])).toEqual([
|
|
534
|
+
'policy/no-mocking is enabled by no top-level or override rules record',
|
|
535
|
+
])
|
|
536
|
+
})
|
|
537
|
+
|
|
538
|
+
it('reports a population no override files list declares exactly', () => {
|
|
539
|
+
const configuration = { rules: {}, overrides: [{ files: ['app/**/*.ts'], rules: {} }] }
|
|
540
|
+
expect(inspectPolicyWiring(configuration, [], [['src/**/*.ts', 'app/**/*.ts']])).toEqual([
|
|
541
|
+
'no override files list declares the population exactly: src/**/*.ts app/**/*.ts',
|
|
542
|
+
])
|
|
543
|
+
})
|
|
544
|
+
|
|
545
|
+
it('reports a configuration that is not a record', () => {
|
|
546
|
+
expect(inspectPolicyWiring([], [], [])).toEqual(['Oxlint configuration must be a record'])
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
it('admits a plain record and refuses an array, null, and a primitive', () => {
|
|
550
|
+
expect(isPolicyRecord({})).toBe(true)
|
|
551
|
+
expect(isPolicyRecord([])).toBe(false)
|
|
552
|
+
expect(isPolicyRecord(null)).toBe(false)
|
|
553
|
+
expect(isPolicyRecord('record')).toBe(false)
|
|
554
|
+
})
|
|
555
|
+
})
|