@ryuenn3123/agentic-senior-core 2.0.5 → 2.0.8
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/.agent-context/blueprints/mobile-app.md +91 -21
- package/.agent-context/profiles/platform.md +13 -13
- package/.agent-context/profiles/regulated.md +13 -13
- package/.agent-context/profiles/startup.md +13 -13
- package/.agent-context/prompts/review-code.md +3 -3
- package/.agent-context/review-checklists/frontend-skill-parity.md +28 -28
- package/.agent-context/review-checklists/frontend-usability.md +33 -33
- package/.agent-context/review-checklists/pr-checklist.md +11 -6
- package/.agent-context/review-checklists/release-operations.md +29 -29
- package/.agent-context/rules/api-docs.md +34 -0
- package/.agent-context/skills/README.md +62 -62
- package/.agent-context/skills/backend/README.md +67 -67
- package/.agent-context/skills/backend/architecture.md +360 -360
- package/.agent-context/skills/backend/compatibility-manifest.json +8 -8
- package/.agent-context/skills/backend/data-access.md +230 -230
- package/.agent-context/skills/backend/errors.md +137 -137
- package/.agent-context/skills/backend/validation.md +116 -116
- package/.agent-context/skills/backend.md +28 -28
- package/.agent-context/skills/cli/README.md +55 -49
- package/.agent-context/skills/cli/compatibility-manifest.json +8 -8
- package/.agent-context/skills/cli/init.md +37 -37
- package/.agent-context/skills/cli/output.md +35 -35
- package/.agent-context/skills/cli/safety-telemetry.md +39 -0
- package/.agent-context/skills/cli/upgrade.md +37 -37
- package/.agent-context/skills/cli.md +31 -28
- package/.agent-context/skills/distribution/.evidence/compatibility-manifest.json +9 -0
- package/.agent-context/skills/distribution/.evidence/sbom-excerpt.json +6 -0
- package/.agent-context/skills/distribution/.evidence/test-report.json +8 -0
- package/.agent-context/skills/distribution/CHANGELOG.md +7 -0
- package/.agent-context/skills/distribution/README.md +27 -19
- package/.agent-context/skills/distribution/compatibility-manifest.json +8 -8
- package/.agent-context/skills/distribution/compatibility.md +31 -31
- package/.agent-context/skills/distribution/package.json +5 -0
- package/.agent-context/skills/distribution/provenance-attestation.md +47 -0
- package/.agent-context/skills/distribution/publish.md +36 -36
- package/.agent-context/skills/distribution/rollback.md +31 -31
- package/.agent-context/skills/distribution/tests/.gitkeep +1 -0
- package/.agent-context/skills/distribution.md +31 -28
- package/.agent-context/skills/frontend/.evidence/compatibility-manifest.json +9 -0
- package/.agent-context/skills/frontend/.evidence/sbom-excerpt.json +6 -0
- package/.agent-context/skills/frontend/.evidence/test-report.json +8 -0
- package/.agent-context/skills/frontend/CHANGELOG.md +7 -0
- package/.agent-context/skills/frontend/README.md +49 -36
- package/.agent-context/skills/frontend/accessibility.md +107 -107
- package/.agent-context/skills/frontend/compatibility-manifest.json +8 -8
- package/.agent-context/skills/frontend/conversion-clarity.md +51 -0
- package/.agent-context/skills/frontend/motion.md +66 -66
- package/.agent-context/skills/frontend/package.json +5 -0
- package/.agent-context/skills/frontend/performance.md +62 -62
- package/.agent-context/skills/frontend/responsive-delivery.md +41 -0
- package/.agent-context/skills/frontend/tests/.gitkeep +1 -0
- package/.agent-context/skills/frontend/ui-architecture.md +128 -128
- package/.agent-context/skills/frontend.md +35 -29
- package/.agent-context/skills/fullstack/.evidence/compatibility-manifest.json +9 -0
- package/.agent-context/skills/fullstack/.evidence/sbom-excerpt.json +6 -0
- package/.agent-context/skills/fullstack/.evidence/test-report.json +8 -0
- package/.agent-context/skills/fullstack/CHANGELOG.md +7 -0
- package/.agent-context/skills/fullstack/README.md +27 -19
- package/.agent-context/skills/fullstack/compatibility-manifest.json +8 -8
- package/.agent-context/skills/fullstack/contracts.md +52 -52
- package/.agent-context/skills/fullstack/end-to-end.md +41 -41
- package/.agent-context/skills/fullstack/feature-slicing.md +64 -64
- package/.agent-context/skills/fullstack/package.json +5 -0
- package/.agent-context/skills/fullstack/release-coordination.md +51 -0
- package/.agent-context/skills/fullstack/tests/.gitkeep +1 -0
- package/.agent-context/skills/fullstack.md +29 -26
- package/.agent-context/skills/index.json +107 -107
- package/.agent-context/skills/review-quality/.evidence/compatibility-manifest.json +9 -0
- package/.agent-context/skills/review-quality/.evidence/sbom-excerpt.json +6 -0
- package/.agent-context/skills/review-quality/.evidence/test-report.json +8 -0
- package/.agent-context/skills/review-quality/CHANGELOG.md +7 -0
- package/.agent-context/skills/review-quality/README.md +27 -19
- package/.agent-context/skills/review-quality/benchmark.md +29 -29
- package/.agent-context/skills/review-quality/compatibility-manifest.json +8 -8
- package/.agent-context/skills/review-quality/package.json +5 -0
- package/.agent-context/skills/review-quality/planning.md +37 -37
- package/.agent-context/skills/review-quality/release-decision.md +49 -0
- package/.agent-context/skills/review-quality/security.md +33 -33
- package/.agent-context/skills/review-quality/tests/.gitkeep +1 -0
- package/.agent-context/skills/review-quality.md +33 -27
- package/.agent-context/stacks/flutter.md +16 -16
- package/.agent-context/stacks/react-native.md +16 -16
- package/.agent-context/state/architecture-map.md +25 -25
- package/.agent-context/state/benchmark-analysis.json +431 -431
- package/.agent-context/state/benchmark-thresholds.json +10 -10
- package/.agent-context/state/benchmark-watchlist.json +19 -19
- package/.agent-context/state/dependency-map.md +32 -32
- package/.agent-context/state/quality-trend-report.json +16 -6
- package/.agent-context/state/skill-platform.json +38 -38
- package/.agent-context/state/weekly-governance-report.json +126 -0
- package/.agent-override.md +36 -36
- package/.cursorrules +1 -1
- package/.gemini/instructions.md +20 -20
- package/.github/ISSUE_TEMPLATE/v1.7-frontend-work-item.yml +54 -54
- package/.github/copilot-instructions.md +21 -21
- package/.github/workflows/benchmark-detection.yml +38 -38
- package/.github/workflows/benchmark-intelligence.yml +50 -50
- package/.github/workflows/frontend-usability-gate.yml +36 -36
- package/.github/workflows/governance-weekly-report.yml +43 -0
- package/.github/workflows/release-gate.yml +32 -32
- package/.github/workflows/sbom-compliance.yml +32 -32
- package/.windsurfrules +1 -1
- package/AGENTS.md +27 -27
- package/README.md +389 -368
- package/lib/cli/commands/init.mjs +13 -1
- package/lib/cli/commands/optimize.mjs +171 -171
- package/lib/cli/commands/upgrade.mjs +9 -1
- package/lib/cli/compatibility.mjs +124 -124
- package/lib/cli/constants.mjs +37 -2
- package/lib/cli/token-optimization.mjs +275 -275
- package/lib/cli/utils.mjs +24 -3
- package/mcp.json +92 -92
- package/package.json +2 -1
- package/scripts/benchmark-gate.mjs +121 -121
- package/scripts/benchmark-intelligence.mjs +140 -140
- package/scripts/detection-benchmark.mjs +138 -138
- package/scripts/frontend-usability-audit.mjs +87 -87
- package/scripts/generate-sbom.mjs +61 -61
- package/scripts/governance-weekly-report.mjs +293 -0
- package/scripts/init-project.ps1 +104 -104
- package/scripts/llm-judge.mjs +664 -664
- package/scripts/quality-trend-report.mjs +288 -288
- package/scripts/release-gate.mjs +261 -259
- package/scripts/skill-tier-policy.mjs +75 -75
- package/scripts/token-optimization-benchmark.mjs +252 -252
- package/scripts/validate.mjs +942 -865
package/scripts/validate.mjs
CHANGED
|
@@ -1,865 +1,942 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* validate.mjs — Repository Integrity Validator
|
|
5
|
-
*
|
|
6
|
-
* Validates the Agentic-Senior-Core repository:
|
|
7
|
-
* - Required files exist
|
|
8
|
-
* - Markdown and JSON documents are readable
|
|
9
|
-
* - Cross-references resolve from the correct source directory
|
|
10
|
-
* - Version references stay consistent for release builds
|
|
11
|
-
* - LLM Judge policy configuration is valid
|
|
12
|
-
*
|
|
13
|
-
* Usage: node scripts/validate.mjs
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
17
|
-
import { dirname, join, relative, resolve } from 'node:path';
|
|
18
|
-
import { fileURLToPath } from 'node:url';
|
|
19
|
-
import { createHash } from 'node:crypto';
|
|
20
|
-
import { validateSkillTopicContent } from './skill-tier-policy.mjs';
|
|
21
|
-
import { calculateTrustScore } from './trust-scorer.mjs';
|
|
22
|
-
|
|
23
|
-
const SCRIPT_FILE_PATH = fileURLToPath(import.meta.url);
|
|
24
|
-
const ROOT_DIR = resolve(dirname(SCRIPT_FILE_PATH), '..');
|
|
25
|
-
const AGENT_CONTEXT_DIR = join(ROOT_DIR, '.agent-context');
|
|
26
|
-
const CANONICAL_INSTRUCTION_PATH = join(ROOT_DIR, '.instructions.md');
|
|
27
|
-
const PACKAGE_JSON_PATH = join(ROOT_DIR, 'package.json');
|
|
28
|
-
const CHANGELOG_PATH = join(ROOT_DIR, 'CHANGELOG.md');
|
|
29
|
-
const README_PATH = join(ROOT_DIR, 'README.md');
|
|
30
|
-
const POLICY_FILE_PATH = join(ROOT_DIR, '.agent-context', 'policies', 'llm-judge-threshold.json');
|
|
31
|
-
const OVERRIDE_FILE_PATH = join(ROOT_DIR, '.agent-override.md');
|
|
32
|
-
const SKILLS_DIR = join(AGENT_CONTEXT_DIR, 'skills');
|
|
33
|
-
const GENERATED_RULE_FILES = ['.cursorrules', '.windsurfrules'];
|
|
34
|
-
const ALLOWED_SEVERITIES = new Set(['critical', 'high', 'medium', 'low']);
|
|
35
|
-
const OVERRIDE_WARNING_WINDOW_DAYS = 30;
|
|
36
|
-
const SUPPORTED_COMPATIBILITY_PLATFORMS = new Set(['windows', 'linux', 'macos']);
|
|
37
|
-
const THIN_ADAPTER_PATHS = [
|
|
38
|
-
'AGENTS.md',
|
|
39
|
-
'.github/copilot-instructions.md',
|
|
40
|
-
'.gemini/instructions.md',
|
|
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
|
-
const
|
|
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
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
if (!
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
'
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
if (typeof
|
|
584
|
-
fail('
|
|
585
|
-
} else {
|
|
586
|
-
pass(`
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
if (
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
fail(
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
'
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
fail(`
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
if (
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
console.
|
|
864
|
-
|
|
865
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* validate.mjs — Repository Integrity Validator
|
|
5
|
+
*
|
|
6
|
+
* Validates the Agentic-Senior-Core repository:
|
|
7
|
+
* - Required files exist
|
|
8
|
+
* - Markdown and JSON documents are readable
|
|
9
|
+
* - Cross-references resolve from the correct source directory
|
|
10
|
+
* - Version references stay consistent for release builds
|
|
11
|
+
* - LLM Judge policy configuration is valid
|
|
12
|
+
*
|
|
13
|
+
* Usage: node scripts/validate.mjs
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
17
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
import { createHash } from 'node:crypto';
|
|
20
|
+
import { validateSkillTopicContent } from './skill-tier-policy.mjs';
|
|
21
|
+
import { calculateTrustScore } from './trust-scorer.mjs';
|
|
22
|
+
|
|
23
|
+
const SCRIPT_FILE_PATH = fileURLToPath(import.meta.url);
|
|
24
|
+
const ROOT_DIR = resolve(dirname(SCRIPT_FILE_PATH), '..');
|
|
25
|
+
const AGENT_CONTEXT_DIR = join(ROOT_DIR, '.agent-context');
|
|
26
|
+
const CANONICAL_INSTRUCTION_PATH = join(ROOT_DIR, '.instructions.md');
|
|
27
|
+
const PACKAGE_JSON_PATH = join(ROOT_DIR, 'package.json');
|
|
28
|
+
const CHANGELOG_PATH = join(ROOT_DIR, 'CHANGELOG.md');
|
|
29
|
+
const README_PATH = join(ROOT_DIR, 'README.md');
|
|
30
|
+
const POLICY_FILE_PATH = join(ROOT_DIR, '.agent-context', 'policies', 'llm-judge-threshold.json');
|
|
31
|
+
const OVERRIDE_FILE_PATH = join(ROOT_DIR, '.agent-override.md');
|
|
32
|
+
const SKILLS_DIR = join(AGENT_CONTEXT_DIR, 'skills');
|
|
33
|
+
const GENERATED_RULE_FILES = ['.cursorrules', '.windsurfrules'];
|
|
34
|
+
const ALLOWED_SEVERITIES = new Set(['critical', 'high', 'medium', 'low']);
|
|
35
|
+
const OVERRIDE_WARNING_WINDOW_DAYS = 30;
|
|
36
|
+
const SUPPORTED_COMPATIBILITY_PLATFORMS = new Set(['windows', 'linux', 'macos']);
|
|
37
|
+
const THIN_ADAPTER_PATHS = [
|
|
38
|
+
'AGENTS.md',
|
|
39
|
+
'.github/copilot-instructions.md',
|
|
40
|
+
'.gemini/instructions.md',
|
|
41
|
+
];
|
|
42
|
+
const FORMAL_ARTIFACT_PATHS = [
|
|
43
|
+
'.instructions.md',
|
|
44
|
+
'README.md',
|
|
45
|
+
'CHANGELOG.md',
|
|
46
|
+
'docs/deep_analysis_and_roadmap_backlog.md',
|
|
47
|
+
'.agent-context/rules/api-docs.md',
|
|
48
|
+
'.agent-context/review-checklists/pr-checklist.md',
|
|
49
|
+
'.agent-context/prompts/review-code.md',
|
|
50
|
+
'.agent-context/skills/review-quality.md',
|
|
51
|
+
'AGENTS.md',
|
|
52
|
+
'.github/copilot-instructions.md',
|
|
53
|
+
'.gemini/instructions.md',
|
|
54
|
+
];
|
|
55
|
+
const REQUIRED_HUMAN_WRITING_SNIPPETS = [
|
|
56
|
+
{
|
|
57
|
+
path: '.agent-context/rules/api-docs.md',
|
|
58
|
+
snippets: ['## Human Writing Standard (Mandatory)', 'No emoji in formal artifacts.'],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
path: '.agent-context/review-checklists/pr-checklist.md',
|
|
62
|
+
snippets: ['No emoji in formal documentation or review summaries', 'Documentation uses plain English and avoids AI cliches'],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
path: 'docs/deep_analysis_and_roadmap_backlog.md',
|
|
66
|
+
snippets: ['## Part 6: Documentation and Explanation Standards (Mandatory)', 'No emoji in formal artifacts. This is mandatory.'],
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
const validationResult = {
|
|
71
|
+
passed: 0,
|
|
72
|
+
failed: 0,
|
|
73
|
+
errors: [],
|
|
74
|
+
warnings: [],
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
async function fileExists(filePath) {
|
|
78
|
+
try {
|
|
79
|
+
await stat(filePath);
|
|
80
|
+
return true;
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function readTextFile(filePath) {
|
|
87
|
+
return readFile(filePath, 'utf8');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function collectFiles(directoryPath, fileExtensionMatcher) {
|
|
91
|
+
const matchingFilePaths = [];
|
|
92
|
+
|
|
93
|
+
async function walk(currentDirectoryPath) {
|
|
94
|
+
const directoryEntries = await readdir(currentDirectoryPath, { withFileTypes: true });
|
|
95
|
+
|
|
96
|
+
for (const directoryEntry of directoryEntries) {
|
|
97
|
+
if (
|
|
98
|
+
directoryEntry.name === '.git'
|
|
99
|
+
|| directoryEntry.name === 'node_modules'
|
|
100
|
+
|| directoryEntry.name === '.agentic-backup'
|
|
101
|
+
|| directoryEntry.name === '.benchmarks'
|
|
102
|
+
) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const entryPath = join(currentDirectoryPath, directoryEntry.name);
|
|
107
|
+
|
|
108
|
+
if (directoryEntry.isDirectory()) {
|
|
109
|
+
await walk(entryPath);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (fileExtensionMatcher(directoryEntry.name)) {
|
|
114
|
+
matchingFilePaths.push(entryPath);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await walk(directoryPath);
|
|
120
|
+
return matchingFilePaths;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function pass(message) {
|
|
124
|
+
validationResult.passed += 1;
|
|
125
|
+
console.log(` PASS ${message}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function fail(message) {
|
|
129
|
+
validationResult.failed += 1;
|
|
130
|
+
validationResult.errors.push(message);
|
|
131
|
+
console.log(` FAIL ${message}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function warn(message) {
|
|
135
|
+
validationResult.warnings.push(message);
|
|
136
|
+
console.log(` WARN ${message}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function normalizeLineEndings(content) {
|
|
140
|
+
return content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function validateRequiredFiles() {
|
|
144
|
+
console.log('\nChecking required files...');
|
|
145
|
+
|
|
146
|
+
const requiredFiles = [
|
|
147
|
+
'bin/agentic-senior-core.js',
|
|
148
|
+
'scripts/validate.mjs',
|
|
149
|
+
'scripts/llm-judge.mjs',
|
|
150
|
+
'scripts/detection-benchmark.mjs',
|
|
151
|
+
'scripts/benchmark-gate.mjs',
|
|
152
|
+
'scripts/benchmark-intelligence.mjs',
|
|
153
|
+
'scripts/governance-weekly-report.mjs',
|
|
154
|
+
'scripts/frontend-usability-audit.mjs',
|
|
155
|
+
'scripts/release-gate.mjs',
|
|
156
|
+
'scripts/generate-sbom.mjs',
|
|
157
|
+
'scripts/init-project.sh',
|
|
158
|
+
'scripts/init-project.ps1',
|
|
159
|
+
'.cursorrules',
|
|
160
|
+
'.windsurfrules',
|
|
161
|
+
'.agent-override.md',
|
|
162
|
+
'.agent-context/policies/llm-judge-threshold.json',
|
|
163
|
+
'mcp.json',
|
|
164
|
+
'AGENTS.md',
|
|
165
|
+
'.github/copilot-instructions.md',
|
|
166
|
+
'.gemini/instructions.md',
|
|
167
|
+
'README.md',
|
|
168
|
+
'CHANGELOG.md',
|
|
169
|
+
'docs/faq.md',
|
|
170
|
+
'docs/deep-dive.md',
|
|
171
|
+
'docs/v1.7-execution-playbook.md',
|
|
172
|
+
'docs/v1.7-issue-breakdown.md',
|
|
173
|
+
'docs/v1.8-operations-playbook.md',
|
|
174
|
+
'docs/v2-upgrade-playbook.md',
|
|
175
|
+
'.agent-context/state/benchmark-watchlist.json',
|
|
176
|
+
'.agent-context/state/skill-platform.json',
|
|
177
|
+
'.agent-context/skills/index.json',
|
|
178
|
+
'.github/workflows/release-gate.yml',
|
|
179
|
+
'.github/workflows/sbom-compliance.yml',
|
|
180
|
+
'.github/workflows/benchmark-intelligence.yml',
|
|
181
|
+
'.github/workflows/governance-weekly-report.yml',
|
|
182
|
+
'tests/cli-smoke.test.mjs',
|
|
183
|
+
'tests/llm-judge.test.mjs',
|
|
184
|
+
'tests/enterprise-ops.test.mjs',
|
|
185
|
+
'LICENSE',
|
|
186
|
+
'.gitignore',
|
|
187
|
+
'.agent-context/marketplace/trust-tiers.json',
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
for (const requiredFilePath of requiredFiles) {
|
|
191
|
+
const absoluteRequiredFilePath = join(ROOT_DIR, requiredFilePath);
|
|
192
|
+
|
|
193
|
+
if (await fileExists(absoluteRequiredFilePath)) {
|
|
194
|
+
pass(requiredFilePath);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
fail(`Missing required file: ${requiredFilePath}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function validateMarkdownFiles() {
|
|
203
|
+
console.log('\nChecking markdown content...');
|
|
204
|
+
|
|
205
|
+
const markdownFilePaths = await collectFiles(ROOT_DIR, (fileName) => fileName.endsWith('.md'));
|
|
206
|
+
|
|
207
|
+
for (const markdownFilePath of markdownFilePaths) {
|
|
208
|
+
const markdownContent = await readTextFile(markdownFilePath);
|
|
209
|
+
const relativeMarkdownPath = relative(ROOT_DIR, markdownFilePath);
|
|
210
|
+
|
|
211
|
+
if (markdownContent.trim().length === 0) {
|
|
212
|
+
fail(`Empty markdown file: ${relativeMarkdownPath}`);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
pass(`${relativeMarkdownPath} (${markdownContent.length} chars)`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function validateRuleFiles() {
|
|
221
|
+
console.log('\nChecking rule, stack, blueprint, checklist, and state files...');
|
|
222
|
+
|
|
223
|
+
const expectedPaths = [
|
|
224
|
+
'rules/naming-conv.md',
|
|
225
|
+
'rules/architecture.md',
|
|
226
|
+
'rules/security.md',
|
|
227
|
+
'rules/performance.md',
|
|
228
|
+
'rules/error-handling.md',
|
|
229
|
+
'rules/testing.md',
|
|
230
|
+
'rules/git-workflow.md',
|
|
231
|
+
'rules/efficiency-vs-hype.md',
|
|
232
|
+
'rules/api-docs.md',
|
|
233
|
+
'rules/microservices.md',
|
|
234
|
+
'rules/event-driven.md',
|
|
235
|
+
'rules/database-design.md',
|
|
236
|
+
'rules/realtime.md',
|
|
237
|
+
'rules/frontend-architecture.md',
|
|
238
|
+
'stacks/typescript.md',
|
|
239
|
+
'stacks/python.md',
|
|
240
|
+
'stacks/java.md',
|
|
241
|
+
'stacks/php.md',
|
|
242
|
+
'stacks/go.md',
|
|
243
|
+
'stacks/csharp.md',
|
|
244
|
+
'stacks/rust.md',
|
|
245
|
+
'stacks/ruby.md',
|
|
246
|
+
'blueprints/api-nextjs.md',
|
|
247
|
+
'blueprints/nestjs-logic.md',
|
|
248
|
+
'blueprints/fastapi-service.md',
|
|
249
|
+
'blueprints/laravel-api.md',
|
|
250
|
+
'blueprints/spring-boot-api.md',
|
|
251
|
+
'blueprints/go-service.md',
|
|
252
|
+
'blueprints/aspnet-api.md',
|
|
253
|
+
'blueprints/ci-github-actions.md',
|
|
254
|
+
'blueprints/ci-gitlab.md',
|
|
255
|
+
'blueprints/observability.md',
|
|
256
|
+
'blueprints/graphql-grpc-api.md',
|
|
257
|
+
'blueprints/infrastructure-as-code.md',
|
|
258
|
+
'blueprints/kubernetes-manifests.md',
|
|
259
|
+
'profiles/startup.md',
|
|
260
|
+
'profiles/regulated.md',
|
|
261
|
+
'profiles/platform.md',
|
|
262
|
+
'review-checklists/pr-checklist.md',
|
|
263
|
+
'review-checklists/frontend-usability.md',
|
|
264
|
+
'review-checklists/frontend-skill-parity.md',
|
|
265
|
+
'review-checklists/release-operations.md',
|
|
266
|
+
'review-checklists/security-audit.md',
|
|
267
|
+
'review-checklists/performance-audit.md',
|
|
268
|
+
'review-checklists/architecture-review.md',
|
|
269
|
+
'review-checklists/marketplace-acceptance.md',
|
|
270
|
+
'skills/README.md',
|
|
271
|
+
'skills/frontend/README.md',
|
|
272
|
+
'skills/backend/README.md',
|
|
273
|
+
'skills/fullstack/README.md',
|
|
274
|
+
'skills/cli/README.md',
|
|
275
|
+
'skills/distribution/README.md',
|
|
276
|
+
'skills/review-quality/README.md',
|
|
277
|
+
'skills/frontend.md',
|
|
278
|
+
'skills/backend.md',
|
|
279
|
+
'skills/fullstack.md',
|
|
280
|
+
'skills/cli.md',
|
|
281
|
+
'skills/distribution.md',
|
|
282
|
+
'skills/review-quality.md',
|
|
283
|
+
'state/architecture-map.md',
|
|
284
|
+
'state/dependency-map.md',
|
|
285
|
+
];
|
|
286
|
+
|
|
287
|
+
for (const expectedPath of expectedPaths) {
|
|
288
|
+
const absoluteExpectedPath = join(AGENT_CONTEXT_DIR, expectedPath);
|
|
289
|
+
|
|
290
|
+
if (!(await fileExists(absoluteExpectedPath))) {
|
|
291
|
+
fail(`Missing agent context file: .agent-context/${expectedPath}`);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const fileContent = await readTextFile(absoluteExpectedPath);
|
|
296
|
+
if (fileContent.trim().length < 100) {
|
|
297
|
+
fail(`Agent context file is suspiciously short: .agent-context/${expectedPath}`);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
pass(`.agent-context/${expectedPath}`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function validateSkillTierQuality() {
|
|
306
|
+
console.log('\nChecking skill tier quality...');
|
|
307
|
+
|
|
308
|
+
const skillMarkdownFiles = await collectFiles(SKILLS_DIR, (fileName) => fileName.endsWith('.md'));
|
|
309
|
+
const scopedSkillTopicFiles = skillMarkdownFiles.filter((skillFilePath) => {
|
|
310
|
+
if (skillFilePath.endsWith('README.md') || skillFilePath.endsWith('CHANGELOG.md')) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const relativeSkillPath = relative(SKILLS_DIR, skillFilePath);
|
|
315
|
+
return /[\\/]/.test(relativeSkillPath);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
for (const skillTopicPath of scopedSkillTopicFiles) {
|
|
319
|
+
const skillTopicContent = await readTextFile(skillTopicPath);
|
|
320
|
+
const relativeSkillTopicPath = relative(ROOT_DIR, skillTopicPath);
|
|
321
|
+
const validationResult = validateSkillTopicContent(skillTopicContent);
|
|
322
|
+
|
|
323
|
+
if (!validationResult.isValid) {
|
|
324
|
+
if (validationResult.reason === 'missing-tier') {
|
|
325
|
+
fail(`${relativeSkillTopicPath} is missing explicit Tier metadata`);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (validationResult.reason === 'unsupported-tier') {
|
|
330
|
+
fail(`${relativeSkillTopicPath} has unsupported tier: ${validationResult.detectedTier}`);
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (validationResult.reason === 'word-count') {
|
|
335
|
+
fail(`${relativeSkillTopicPath} tier ${validationResult.detectedTier} must include at least ${validationResult.minimumRules.minWords} words (found ${validationResult.wordCount})`);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (validationResult.reason === 'heading-count') {
|
|
340
|
+
fail(`${relativeSkillTopicPath} tier ${validationResult.detectedTier} must include at least ${validationResult.minimumRules.minHeadings} section headings (found ${validationResult.headingCount})`);
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (validationResult.reason === 'checklist-count') {
|
|
345
|
+
fail(`${relativeSkillTopicPath} tier ${validationResult.detectedTier} must include at least ${validationResult.minimumRules.minChecklistItems} checklist item(s) (found ${validationResult.checklistCount})`);
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (validationResult.reason === 'code-block-count') {
|
|
350
|
+
fail(`${relativeSkillTopicPath} tier ${validationResult.detectedTier} must include at least ${validationResult.minimumRules.minCodeBlocks} code block(s) (found ${validationResult.codeBlockCount})`);
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
fail(`${relativeSkillTopicPath} failed tier validation`);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
pass(`${relativeSkillTopicPath} tier ${validationResult.detectedTier} quality gate passed`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async function validateSkillCompatibilityManifests() {
|
|
363
|
+
console.log('\nChecking skill compatibility manifests...');
|
|
364
|
+
|
|
365
|
+
const skillDomainEntries = await readdir(SKILLS_DIR, { withFileTypes: true });
|
|
366
|
+
const skillDomainDirectoryNames = skillDomainEntries
|
|
367
|
+
.filter((entry) => entry.isDirectory())
|
|
368
|
+
.map((entry) => entry.name)
|
|
369
|
+
.sort((leftName, rightName) => leftName.localeCompare(rightName));
|
|
370
|
+
|
|
371
|
+
let validManifestCount = 0;
|
|
372
|
+
|
|
373
|
+
for (const skillDomainDirectoryName of skillDomainDirectoryNames) {
|
|
374
|
+
const compatibilityManifestPath = join(
|
|
375
|
+
SKILLS_DIR,
|
|
376
|
+
skillDomainDirectoryName,
|
|
377
|
+
'compatibility-manifest.json'
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
if (!(await fileExists(compatibilityManifestPath))) {
|
|
381
|
+
fail(`Missing compatibility manifest: .agent-context/skills/${skillDomainDirectoryName}/compatibility-manifest.json`);
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
let parsedCompatibilityManifest;
|
|
386
|
+
try {
|
|
387
|
+
parsedCompatibilityManifest = JSON.parse(await readTextFile(compatibilityManifestPath));
|
|
388
|
+
} catch (error) {
|
|
389
|
+
fail(`Invalid JSON compatibility manifest for ${skillDomainDirectoryName}: ${error.message}`);
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (!Array.isArray(parsedCompatibilityManifest.ides) || parsedCompatibilityManifest.ides.length === 0) {
|
|
394
|
+
fail(`Compatibility manifest for ${skillDomainDirectoryName} must include non-empty "ides" array`);
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (!Array.isArray(parsedCompatibilityManifest.platforms) || parsedCompatibilityManifest.platforms.length === 0) {
|
|
399
|
+
fail(`Compatibility manifest for ${skillDomainDirectoryName} must include non-empty "platforms" array`);
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const unsupportedPlatform = parsedCompatibilityManifest.platforms.find(
|
|
404
|
+
(platformName) => !SUPPORTED_COMPATIBILITY_PLATFORMS.has(platformName)
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
if (unsupportedPlatform) {
|
|
408
|
+
fail(`Compatibility manifest for ${skillDomainDirectoryName} has unsupported platform: ${unsupportedPlatform}`);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (
|
|
413
|
+
typeof parsedCompatibilityManifest.nodeMin !== 'string'
|
|
414
|
+
|| !/^\d+(\.\d+)?$/.test(parsedCompatibilityManifest.nodeMin)
|
|
415
|
+
) {
|
|
416
|
+
fail(`Compatibility manifest for ${skillDomainDirectoryName} must include string nodeMin (for example "18" or "18.0")`);
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
validManifestCount += 1;
|
|
421
|
+
pass(`Compatibility manifest validated: .agent-context/skills/${skillDomainDirectoryName}/compatibility-manifest.json`);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (validManifestCount >= 6) {
|
|
425
|
+
pass(`Compatibility manifest coverage is valid (${validManifestCount} skill domains)`);
|
|
426
|
+
} else {
|
|
427
|
+
fail(`Compatibility manifest coverage is insufficient (${validManifestCount}/6 skill domains)`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function stripMarkdownCodeBlocks(markdownText) {
|
|
432
|
+
return markdownText.replace(/```[\s\S]*?```/g, '');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function parseOverrideExpiryDate(rawExpiryValue) {
|
|
436
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(rawExpiryValue)) {
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const parsedDate = new Date(`${rawExpiryValue}T00:00:00.000Z`);
|
|
441
|
+
return Number.isNaN(parsedDate.getTime()) ? null : parsedDate;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async function validateOverrideGovernance() {
|
|
445
|
+
console.log('\nChecking override governance...');
|
|
446
|
+
|
|
447
|
+
const overrideContent = await readTextFile(OVERRIDE_FILE_PATH);
|
|
448
|
+
const overrideContentWithoutCodeBlocks = stripMarkdownCodeBlocks(overrideContent);
|
|
449
|
+
const overrideEntryPattern = /\[Rule:\s*([^\]]+)\]([\s\S]*?)(?=\n\[Rule:|$)/g;
|
|
450
|
+
const overrideEntries = [];
|
|
451
|
+
let overrideEntryMatch = overrideEntryPattern.exec(overrideContentWithoutCodeBlocks);
|
|
452
|
+
|
|
453
|
+
while (overrideEntryMatch) {
|
|
454
|
+
const ruleName = overrideEntryMatch[1].trim();
|
|
455
|
+
const entryBody = overrideEntryMatch[2];
|
|
456
|
+
const ownerMatch = entryBody.match(/(?:^|\n)Owner:\s*(.+)/);
|
|
457
|
+
const expiryMatch = entryBody.match(/(?:^|\n)Expiry:\s*(.+)/);
|
|
458
|
+
|
|
459
|
+
overrideEntries.push({
|
|
460
|
+
ruleName,
|
|
461
|
+
owner: ownerMatch ? ownerMatch[1].trim() : '',
|
|
462
|
+
expiry: expiryMatch ? expiryMatch[1].trim() : '',
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
overrideEntryMatch = overrideEntryPattern.exec(overrideContentWithoutCodeBlocks);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (overrideEntries.length === 0) {
|
|
469
|
+
pass('No active override entries found; governance baseline remains strict');
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const currentDate = new Date();
|
|
474
|
+
|
|
475
|
+
for (const overrideEntry of overrideEntries) {
|
|
476
|
+
const overrideContextLabel = `[Rule: ${overrideEntry.ruleName}]`;
|
|
477
|
+
|
|
478
|
+
if (!overrideEntry.owner) {
|
|
479
|
+
fail(`${overrideContextLabel} is missing Owner metadata`);
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
pass(`${overrideContextLabel} owner is defined`);
|
|
484
|
+
|
|
485
|
+
if (!overrideEntry.expiry) {
|
|
486
|
+
fail(`${overrideContextLabel} is missing Expiry metadata`);
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const expiryDate = parseOverrideExpiryDate(overrideEntry.expiry);
|
|
491
|
+
if (!expiryDate) {
|
|
492
|
+
fail(`${overrideContextLabel} has invalid Expiry format (expected YYYY-MM-DD)`);
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const remainingMilliseconds = expiryDate.getTime() - currentDate.getTime();
|
|
497
|
+
const remainingDays = Math.floor(remainingMilliseconds / (1000 * 60 * 60 * 24));
|
|
498
|
+
|
|
499
|
+
if (remainingMilliseconds < 0) {
|
|
500
|
+
fail(`${overrideContextLabel} is expired (${overrideEntry.expiry})`);
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
pass(`${overrideContextLabel} expiry is valid (${overrideEntry.expiry})`);
|
|
505
|
+
|
|
506
|
+
if (remainingDays <= OVERRIDE_WARNING_WINDOW_DAYS) {
|
|
507
|
+
warn(`${overrideContextLabel} expires in ${remainingDays} day(s); renew or remove soon`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async function validateCrossReferences() {
|
|
513
|
+
console.log('\nChecking internal links...');
|
|
514
|
+
|
|
515
|
+
const markdownFilePaths = await collectFiles(ROOT_DIR, (fileName) => fileName.endsWith('.md'));
|
|
516
|
+
const linkPattern = /\[([^\]]*)\]\((?!https?:\/\/|#)([^)]+)\)/g;
|
|
517
|
+
let checkedLinkCount = 0;
|
|
518
|
+
|
|
519
|
+
for (const markdownFilePath of markdownFilePaths) {
|
|
520
|
+
const markdownContent = await readTextFile(markdownFilePath);
|
|
521
|
+
const currentFileDirectory = dirname(markdownFilePath);
|
|
522
|
+
const relativeMarkdownPath = relative(ROOT_DIR, markdownFilePath);
|
|
523
|
+
let linkMatch = linkPattern.exec(markdownContent);
|
|
524
|
+
|
|
525
|
+
while (linkMatch) {
|
|
526
|
+
const rawLinkTarget = linkMatch[2].split('#')[0];
|
|
527
|
+
if (rawLinkTarget) {
|
|
528
|
+
checkedLinkCount += 1;
|
|
529
|
+
const resolvedLinkPath = resolve(currentFileDirectory, rawLinkTarget);
|
|
530
|
+
|
|
531
|
+
if (await fileExists(resolvedLinkPath)) {
|
|
532
|
+
pass(`${relativeMarkdownPath} → ${linkMatch[2]}`);
|
|
533
|
+
} else {
|
|
534
|
+
fail(`Broken link in ${relativeMarkdownPath}: ${linkMatch[2]}`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
linkMatch = linkPattern.exec(markdownContent);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (checkedLinkCount === 0) {
|
|
543
|
+
warn('No internal links were found in markdown files');
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
async function validateAgentsManifest() {
|
|
548
|
+
console.log('\nChecking AGENTS.md manifest links...');
|
|
549
|
+
|
|
550
|
+
const agentsContent = await readTextFile(join(ROOT_DIR, 'AGENTS.md'));
|
|
551
|
+
const fileReferencePattern = /\[`?([^`\]]+)`?\]\(([^)]+)\)/g;
|
|
552
|
+
let manifestLinkCount = 0;
|
|
553
|
+
let fileReferenceMatch = fileReferencePattern.exec(agentsContent);
|
|
554
|
+
|
|
555
|
+
while (fileReferenceMatch) {
|
|
556
|
+
const manifestLinkTarget = fileReferenceMatch[2];
|
|
557
|
+
|
|
558
|
+
if (!manifestLinkTarget.startsWith('http')) {
|
|
559
|
+
manifestLinkCount += 1;
|
|
560
|
+
const resolvedManifestLinkPath = resolve(ROOT_DIR, manifestLinkTarget);
|
|
561
|
+
|
|
562
|
+
if (await fileExists(resolvedManifestLinkPath)) {
|
|
563
|
+
pass(`AGENTS.md → ${manifestLinkTarget}`);
|
|
564
|
+
} else {
|
|
565
|
+
fail(`AGENTS.md references missing file: ${manifestLinkTarget}`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
fileReferenceMatch = fileReferencePattern.exec(agentsContent);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (manifestLinkCount === 0) {
|
|
573
|
+
warn('AGENTS.md does not contain any local manifest links');
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
async function validatePackageMetadata() {
|
|
578
|
+
console.log('\nChecking package metadata...');
|
|
579
|
+
|
|
580
|
+
const packageJson = JSON.parse(await readTextFile(PACKAGE_JSON_PATH));
|
|
581
|
+
const versionPattern = /^\d+\.\d+\.\d+$/;
|
|
582
|
+
|
|
583
|
+
if (typeof packageJson.version !== 'string' || !versionPattern.test(packageJson.version)) {
|
|
584
|
+
fail('package.json version must be a semantic version string');
|
|
585
|
+
} else {
|
|
586
|
+
pass(`package.json version ${packageJson.version}`);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (packageJson.scripts?.validate === 'node ./scripts/validate.mjs') {
|
|
590
|
+
pass('package.json validate script is Node-first');
|
|
591
|
+
} else {
|
|
592
|
+
fail('package.json validate script must use node ./scripts/validate.mjs');
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (packageJson.scripts?.test) {
|
|
596
|
+
pass('package.json test script exists');
|
|
597
|
+
} else {
|
|
598
|
+
fail('package.json test script is missing');
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (packageJson.devDependencies && Object.keys(packageJson.devDependencies).length > 0) {
|
|
602
|
+
warn('package.json still has devDependencies; review whether they are necessary');
|
|
603
|
+
} else {
|
|
604
|
+
pass('package.json has no unnecessary devDependencies');
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async function validatePolicyFile() {
|
|
609
|
+
console.log('\nChecking LLM Judge policy...');
|
|
610
|
+
|
|
611
|
+
const policyContent = await readTextFile(POLICY_FILE_PATH);
|
|
612
|
+
const parsedPolicy = JSON.parse(policyContent);
|
|
613
|
+
const selectedProfileName = parsedPolicy.selectedProfile;
|
|
614
|
+
const profileThresholds = parsedPolicy.profileThresholds;
|
|
615
|
+
|
|
616
|
+
if (typeof selectedProfileName !== 'string') {
|
|
617
|
+
fail('Policy file must define selectedProfile as a string');
|
|
618
|
+
} else {
|
|
619
|
+
pass(`LLM Judge selected profile: ${selectedProfileName}`);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (!profileThresholds || typeof profileThresholds !== 'object') {
|
|
623
|
+
fail('Policy file must define profileThresholds');
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
for (const [profileName, profileSettings] of Object.entries(profileThresholds)) {
|
|
628
|
+
if (!Array.isArray(profileSettings.blockingSeverities)) {
|
|
629
|
+
fail(`Policy profile ${profileName} must define blockingSeverities`);
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const invalidSeverity = profileSettings.blockingSeverities.find((severity) => !ALLOWED_SEVERITIES.has(severity));
|
|
634
|
+
if (invalidSeverity) {
|
|
635
|
+
fail(`Policy profile ${profileName} uses unsupported severity: ${invalidSeverity}`);
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
pass(`Policy profile ${profileName} blocking severities are valid`);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (typeof profileThresholds[selectedProfileName] === 'object') {
|
|
643
|
+
pass('Policy selectedProfile points to a valid profile');
|
|
644
|
+
} else {
|
|
645
|
+
fail('Policy selectedProfile must match one of the configured profileThresholds');
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
async function validateVersionConsistency() {
|
|
650
|
+
console.log('\nChecking release version consistency...');
|
|
651
|
+
|
|
652
|
+
const packageJson = JSON.parse(await readTextFile(PACKAGE_JSON_PATH));
|
|
653
|
+
const packageVersion = packageJson.version;
|
|
654
|
+
const changelogContent = await readTextFile(CHANGELOG_PATH);
|
|
655
|
+
|
|
656
|
+
if (changelogContent.includes(`## ${packageVersion}`)) {
|
|
657
|
+
pass(`CHANGELOG.md contains release entry for ${packageVersion}`);
|
|
658
|
+
} else {
|
|
659
|
+
fail(`CHANGELOG.md is missing a ## ${packageVersion} heading`);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
for (const generatedRuleFileName of GENERATED_RULE_FILES) {
|
|
663
|
+
const generatedRuleContent = await readTextFile(join(ROOT_DIR, generatedRuleFileName));
|
|
664
|
+
|
|
665
|
+
if (generatedRuleContent.includes(`Generated by Agentic-Senior-Core CLI v${packageVersion}`)) {
|
|
666
|
+
pass(`${generatedRuleFileName} matches package version ${packageVersion}`);
|
|
667
|
+
} else {
|
|
668
|
+
fail(`${generatedRuleFileName} does not match package version ${packageVersion}`);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
async function validateDocumentationFlow() {
|
|
674
|
+
console.log('\nChecking documentation flow...');
|
|
675
|
+
|
|
676
|
+
const readmeContent = await readTextFile(README_PATH);
|
|
677
|
+
const requiredReadmeSnippets = [
|
|
678
|
+
'GitHub Template',
|
|
679
|
+
'scripts/init-project.ps1',
|
|
680
|
+
'scripts/init-project.sh',
|
|
681
|
+
'npx @ryuenn3123/agentic-senior-core init',
|
|
682
|
+
'npm run validate',
|
|
683
|
+
'docs/faq.md',
|
|
684
|
+
'docs/deep-dive.md',
|
|
685
|
+
'docs/v2-upgrade-playbook.md',
|
|
686
|
+
];
|
|
687
|
+
|
|
688
|
+
for (const requiredReadmeSnippet of requiredReadmeSnippets) {
|
|
689
|
+
if (readmeContent.includes(requiredReadmeSnippet)) {
|
|
690
|
+
pass(`README.md mentions ${requiredReadmeSnippet}`);
|
|
691
|
+
} else {
|
|
692
|
+
fail(`README.md must mention ${requiredReadmeSnippet}`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
async function validateMcpConfiguration() {
|
|
698
|
+
console.log('\nChecking MCP configuration...');
|
|
699
|
+
|
|
700
|
+
const mcpConfiguration = JSON.parse(await readTextFile(join(ROOT_DIR, 'mcp.json')));
|
|
701
|
+
const lintServerCommand = mcpConfiguration.servers?.lint?.command;
|
|
702
|
+
const testServerCommand = mcpConfiguration.servers?.test?.command;
|
|
703
|
+
|
|
704
|
+
if (lintServerCommand === 'node') {
|
|
705
|
+
pass('MCP lint server uses Node');
|
|
706
|
+
} else {
|
|
707
|
+
fail('MCP lint server must use Node');
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (testServerCommand === 'node') {
|
|
711
|
+
pass('MCP test server uses Node');
|
|
712
|
+
} else {
|
|
713
|
+
fail('MCP test server must use Node');
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
async function validateHumanWritingGovernance() {
|
|
718
|
+
console.log('\nChecking human writing governance...');
|
|
719
|
+
|
|
720
|
+
const disallowedEmojiPattern = /[\u2705\u274C\u26A0\u{1F4CC}\u{1F536}\u{1F4CE}\u{1F534}\u{1F7E0}\u{1F7E1}\u{1F7E2}]/u;
|
|
721
|
+
|
|
722
|
+
for (const formalArtifactPath of FORMAL_ARTIFACT_PATHS) {
|
|
723
|
+
const absoluteFormalArtifactPath = join(ROOT_DIR, formalArtifactPath);
|
|
724
|
+
|
|
725
|
+
if (!(await fileExists(absoluteFormalArtifactPath))) {
|
|
726
|
+
fail(`Missing formal artifact for writing governance: ${formalArtifactPath}`);
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const formalArtifactContent = await readTextFile(absoluteFormalArtifactPath);
|
|
731
|
+
|
|
732
|
+
if (disallowedEmojiPattern.test(formalArtifactContent)) {
|
|
733
|
+
fail(`${formalArtifactPath} contains disallowed emoji symbols in formal text`);
|
|
734
|
+
} else {
|
|
735
|
+
pass(`${formalArtifactPath} has no disallowed emoji symbols`);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
for (const snippetRule of REQUIRED_HUMAN_WRITING_SNIPPETS) {
|
|
740
|
+
const absoluteRulePath = join(ROOT_DIR, snippetRule.path);
|
|
741
|
+
if (!(await fileExists(absoluteRulePath))) {
|
|
742
|
+
fail(`Missing writing governance source: ${snippetRule.path}`);
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const writingRuleContent = await readTextFile(absoluteRulePath);
|
|
747
|
+
for (const requiredSnippet of snippetRule.snippets) {
|
|
748
|
+
if (writingRuleContent.includes(requiredSnippet)) {
|
|
749
|
+
pass(`${snippetRule.path} includes writing governance snippet: ${requiredSnippet}`);
|
|
750
|
+
} else {
|
|
751
|
+
fail(`${snippetRule.path} is missing writing governance snippet: ${requiredSnippet}`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
async function validateInstructionAdapters() {
|
|
758
|
+
console.log('\nChecking instruction adapter consolidation...');
|
|
759
|
+
|
|
760
|
+
const canonicalInstructionContent = normalizeLineEndings(await readTextFile(CANONICAL_INSTRUCTION_PATH));
|
|
761
|
+
const canonicalSnapshotHash = createHash('sha256').update(canonicalInstructionContent).digest('hex');
|
|
762
|
+
|
|
763
|
+
for (const thinAdapterPath of THIN_ADAPTER_PATHS) {
|
|
764
|
+
const absoluteAdapterPath = join(ROOT_DIR, thinAdapterPath);
|
|
765
|
+
|
|
766
|
+
if (!(await fileExists(absoluteAdapterPath))) {
|
|
767
|
+
fail(`Missing thin adapter file: ${thinAdapterPath}`);
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
const thinAdapterContent = await readTextFile(absoluteAdapterPath);
|
|
772
|
+
|
|
773
|
+
if (
|
|
774
|
+
thinAdapterContent.includes('Adapter Mode: thin')
|
|
775
|
+
&& thinAdapterContent.includes('Adapter Source: .instructions.md')
|
|
776
|
+
) {
|
|
777
|
+
pass(`${thinAdapterPath} declares thin adapter metadata`);
|
|
778
|
+
} else {
|
|
779
|
+
fail(`${thinAdapterPath} must declare Adapter Mode: thin and Adapter Source: .instructions.md`);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
const hashMatch = thinAdapterContent.match(/Canonical Snapshot SHA256:\s*([a-f0-9]{64})/);
|
|
783
|
+
if (!hashMatch) {
|
|
784
|
+
fail(`${thinAdapterPath} must declare Canonical Snapshot SHA256`);
|
|
785
|
+
continue;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (hashMatch[1] === canonicalSnapshotHash) {
|
|
789
|
+
pass(`${thinAdapterPath} canonical hash matches .instructions.md`);
|
|
790
|
+
} else {
|
|
791
|
+
fail(`${thinAdapterPath} canonical hash drift detected (expected ${canonicalSnapshotHash})`);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const thinAdapterLineCount = thinAdapterContent.split(/\r?\n/u).length;
|
|
795
|
+
if (thinAdapterLineCount <= 80) {
|
|
796
|
+
pass(`${thinAdapterPath} remains thin (${thinAdapterLineCount} lines)`);
|
|
797
|
+
} else {
|
|
798
|
+
fail(`${thinAdapterPath} is too large for thin-adapter mode (${thinAdapterLineCount} lines)`);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
async function validateTrustTierSchema() {
|
|
804
|
+
console.log('\nChecking marketplace trust tier schema...');
|
|
805
|
+
|
|
806
|
+
const trustTierPath = join(AGENT_CONTEXT_DIR, 'marketplace', 'trust-tiers.json');
|
|
807
|
+
const trustTierContent = await readTextFile(trustTierPath);
|
|
808
|
+
const trustTierSchema = JSON.parse(trustTierContent);
|
|
809
|
+
|
|
810
|
+
const expectedTierNames = ['verified', 'community', 'experimental'];
|
|
811
|
+
for (const expectedTierName of expectedTierNames) {
|
|
812
|
+
if (trustTierSchema.tiers?.[expectedTierName]) {
|
|
813
|
+
pass(`Trust tier "${expectedTierName}" is defined`);
|
|
814
|
+
} else {
|
|
815
|
+
fail(`Trust tier "${expectedTierName}" is missing from trust-tiers.json`);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const scorecardDimensions = trustTierSchema.scorecard?.dimensions;
|
|
820
|
+
if (!scorecardDimensions || typeof scorecardDimensions !== 'object') {
|
|
821
|
+
fail('Trust tier scorecard must define dimensions');
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const dimensionNames = Object.keys(scorecardDimensions);
|
|
826
|
+
let totalWeight = 0;
|
|
827
|
+
|
|
828
|
+
for (const dimensionName of dimensionNames) {
|
|
829
|
+
const dimensionWeight = scorecardDimensions[dimensionName].weight;
|
|
830
|
+
if (typeof dimensionWeight !== 'number' || dimensionWeight <= 0) {
|
|
831
|
+
fail(`Scorecard dimension "${dimensionName}" must have a positive weight`);
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
totalWeight += dimensionWeight;
|
|
835
|
+
pass(`Scorecard dimension "${dimensionName}" weight: ${dimensionWeight}`);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (totalWeight === 100) {
|
|
839
|
+
pass(`Scorecard weights sum to 100`);
|
|
840
|
+
} else {
|
|
841
|
+
fail(`Scorecard weights must sum to 100 (got ${totalWeight})`);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
for (const dimensionName of dimensionNames) {
|
|
845
|
+
const gates = scorecardDimensions[dimensionName].gates;
|
|
846
|
+
if (!Array.isArray(gates) || gates.length === 0) {
|
|
847
|
+
fail(`Scorecard dimension "${dimensionName}" must define at least one gate`);
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
pass(`Scorecard dimension "${dimensionName}" has ${gates.length} gates`);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
for (const [tierName, tierDefinition] of Object.entries(trustTierSchema.tiers)) {
|
|
854
|
+
if (typeof tierDefinition.minimumScore !== 'number') {
|
|
855
|
+
fail(`Tier "${tierName}" must define a numeric minimumScore`);
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
pass(`Tier "${tierName}" minimumScore: ${tierDefinition.minimumScore}`);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
async function validateEvidenceBundles() {
|
|
863
|
+
console.log('\nChecking skill evidence bundles and trust scores...');
|
|
864
|
+
|
|
865
|
+
const skillsDir = join(AGENT_CONTEXT_DIR, 'skills');
|
|
866
|
+
const skillDirs = (await readdir(skillsDir, { withFileTypes: true }))
|
|
867
|
+
.filter(dirent => dirent.isDirectory())
|
|
868
|
+
.map(dirent => dirent.name);
|
|
869
|
+
|
|
870
|
+
const requiredVerifiedSkillNames = new Set([
|
|
871
|
+
'cli',
|
|
872
|
+
'frontend',
|
|
873
|
+
'fullstack',
|
|
874
|
+
'distribution',
|
|
875
|
+
'review-quality',
|
|
876
|
+
]);
|
|
877
|
+
|
|
878
|
+
for (const skillName of skillDirs) {
|
|
879
|
+
try {
|
|
880
|
+
const result = await calculateTrustScore(join(skillsDir, skillName));
|
|
881
|
+
|
|
882
|
+
if (requiredVerifiedSkillNames.has(skillName)) {
|
|
883
|
+
if (result.tier === 'verified') {
|
|
884
|
+
pass(`Skill "${skillName}" achieved Verified trust tier (Score: ${result.score})`);
|
|
885
|
+
} else {
|
|
886
|
+
fail(`Skill "${skillName}" failed to reach Verified tier. Got ${result.tier} (Score: ${result.score})`);
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
pass(`Skill "${skillName}" parses successfully as ${result.tier} tier`);
|
|
894
|
+
} catch (err) {
|
|
895
|
+
fail(`Skill "${skillName}" scorer crashed: ${err.message}`);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
async function main() {
|
|
901
|
+
console.log('===============================================');
|
|
902
|
+
console.log(' Agentic-Senior-Core Repository Validator');
|
|
903
|
+
console.log('===============================================');
|
|
904
|
+
|
|
905
|
+
await validateRequiredFiles();
|
|
906
|
+
await validateMarkdownFiles();
|
|
907
|
+
await validateRuleFiles();
|
|
908
|
+
await validateSkillTierQuality();
|
|
909
|
+
await validateSkillCompatibilityManifests();
|
|
910
|
+
await validateOverrideGovernance();
|
|
911
|
+
await validateAgentsManifest();
|
|
912
|
+
await validateCrossReferences();
|
|
913
|
+
await validatePackageMetadata();
|
|
914
|
+
await validatePolicyFile();
|
|
915
|
+
await validateVersionConsistency();
|
|
916
|
+
await validateDocumentationFlow();
|
|
917
|
+
await validateMcpConfiguration();
|
|
918
|
+
await validateHumanWritingGovernance();
|
|
919
|
+
await validateInstructionAdapters();
|
|
920
|
+
await validateTrustTierSchema();
|
|
921
|
+
await validateEvidenceBundles();
|
|
922
|
+
|
|
923
|
+
console.log('\n===============================================');
|
|
924
|
+
console.log(' RESULTS');
|
|
925
|
+
console.log('===============================================');
|
|
926
|
+
console.log(` Passed: ${validationResult.passed}`);
|
|
927
|
+
console.log(` Failed: ${validationResult.failed}`);
|
|
928
|
+
console.log(` Warnings: ${validationResult.warnings.length}`);
|
|
929
|
+
console.log('===============================================');
|
|
930
|
+
|
|
931
|
+
if (validationResult.failed > 0) {
|
|
932
|
+
console.log('\nVALIDATION FAILED\n');
|
|
933
|
+
process.exit(1);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
console.log('\nALL CHECKS PASSED\n');
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
main().catch((error) => {
|
|
940
|
+
console.error('Validator crashed:', error);
|
|
941
|
+
process.exit(1);
|
|
942
|
+
});
|