@plainconceptsplatform/workflows 1.10.1 → 1.12.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.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/dist/catalog-installation.js +5 -0
  3. package/dist/catalog-listing.js +6 -1
  4. package/dist/index.js +27 -4
  5. package/dist/template-bundles.d.ts +21 -0
  6. package/dist/template-bundles.js +108 -0
  7. package/dist/workflow-catalog.d.ts +5 -1
  8. package/dist/workflow-catalog.js +8 -0
  9. package/loops/templates/ci/app-ci-dotnet-next.yml +778 -117
  10. package/loops/templates/platform-template/.github/workflows/platform-template-sync.yml +202 -0
  11. package/loops/templates/platform-template/scripts/check-platform-conformance.mjs +453 -0
  12. package/loops/templates/quality-gates/.jscpd.json +36 -0
  13. package/loops/templates/quality-gates/.jscpd.json.md +7 -0
  14. package/loops/templates/quality-gates/.semgrep/platform-hygiene.yml +88 -0
  15. package/loops/templates/quality-gates/.zap/rules.tsv +26 -0
  16. package/loops/templates/quality-gates/scripts/check-complexity.mjs +86 -0
  17. package/loops/templates/quality-gates/scripts/check-coverage.mjs +89 -0
  18. package/loops/templates/quality-gates/scripts/check-duplication.mjs +67 -0
  19. package/loops/templates/quality-gates/scripts/check-mutation.mjs +67 -0
  20. package/loops/templates/quality-gates/scripts/dast-wait-stable.sh +53 -0
  21. package/loops/templates/quality-gates/scripts/engineering-excellence.mjs +188 -0
  22. package/loops/templates/quality-gates/scripts/engineering-excellence.schema.json +895 -0
  23. package/loops/templates/quality-gates/scripts/engineering-excellence.schema.json.md +5 -0
  24. package/loops/templates/quality-gates/scripts/lib/complexity-gate.mjs +80 -0
  25. package/loops/templates/quality-gates/scripts/lib/complexity-gate.test.mjs +105 -0
  26. package/loops/templates/quality-gates/scripts/lib/duplication-gate.mjs +50 -0
  27. package/loops/templates/quality-gates/scripts/lib/duplication-gate.test.mjs +47 -0
  28. package/loops/templates/quality-gates/scripts/lib/engineering-excellence.mjs +674 -0
  29. package/loops/templates/quality-gates/scripts/lib/engineering-excellence.test.mjs +284 -0
  30. package/loops/templates/quality-gates/scripts/lib/hygiene-facet.test.mjs +179 -0
  31. package/loops/templates/quality-gates/scripts/lib/quality-config.mjs +65 -0
  32. package/loops/templates/quality-gates/scripts/lib/quality-config.test.mjs +29 -0
  33. package/loops/templates/quality-gates/scripts/lib/source-files.mjs +26 -0
  34. package/package.json +2 -2
@@ -0,0 +1,895 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/PlainConceptsPlatform/Project-Health/schema/engineering-excellence-facets.schema.json",
4
+ "title": "Engineering Excellence facet",
5
+ "description": "One artifact `engineering-excellence-<facet>` containing `<facet>.json`, uploaded by a repo's main-branch CI and read by the Project-Health collector. Rates and scores are 0..1 fractions. Atlas vendors this copy; see docs/decisions/2026-10-11-engineering-excellence-facets.md.",
6
+ "$ref": "#/$defs/envelope",
7
+ "$defs": {
8
+ "rate": {
9
+ "type": [
10
+ "number",
11
+ "null"
12
+ ],
13
+ "minimum": 0,
14
+ "maximum": 1
15
+ },
16
+ "count": {
17
+ "type": "integer",
18
+ "minimum": 0
19
+ },
20
+ "envelope": {
21
+ "type": "object",
22
+ "required": [
23
+ "schemaVersion",
24
+ "facet",
25
+ "repo",
26
+ "ref",
27
+ "commitSha",
28
+ "runId",
29
+ "generatedAt",
30
+ "data"
31
+ ],
32
+ "additionalProperties": false,
33
+ "properties": {
34
+ "schemaVersion": {
35
+ "type": "integer",
36
+ "enum": [
37
+ 1
38
+ ]
39
+ },
40
+ "facet": {
41
+ "type": "string",
42
+ "enum": [
43
+ "coverage",
44
+ "complexity",
45
+ "mutation",
46
+ "security",
47
+ "sbom",
48
+ "hygiene"
49
+ ]
50
+ },
51
+ "repo": {
52
+ "type": [
53
+ "string",
54
+ "null"
55
+ ]
56
+ },
57
+ "ref": {
58
+ "type": [
59
+ "string",
60
+ "null"
61
+ ]
62
+ },
63
+ "commitSha": {
64
+ "type": [
65
+ "string",
66
+ "null"
67
+ ]
68
+ },
69
+ "runId": {
70
+ "type": [
71
+ "integer",
72
+ "null"
73
+ ]
74
+ },
75
+ "runAttempt": {
76
+ "type": [
77
+ "integer",
78
+ "null"
79
+ ]
80
+ },
81
+ "generatedAt": {
82
+ "type": "string"
83
+ },
84
+ "data": {
85
+ "type": "object"
86
+ }
87
+ }
88
+ },
89
+ "lines": {
90
+ "type": "object",
91
+ "required": [
92
+ "lineRate",
93
+ "branchRate",
94
+ "linesCovered",
95
+ "linesValid"
96
+ ],
97
+ "properties": {
98
+ "lineRate": {
99
+ "$ref": "#/$defs/rate"
100
+ },
101
+ "branchRate": {
102
+ "$ref": "#/$defs/rate"
103
+ },
104
+ "linesCovered": {
105
+ "$ref": "#/$defs/count"
106
+ },
107
+ "linesValid": {
108
+ "$ref": "#/$defs/count"
109
+ }
110
+ }
111
+ },
112
+ "coverage": {
113
+ "type": "object",
114
+ "required": [
115
+ "scopes",
116
+ "global",
117
+ "passed"
118
+ ],
119
+ "additionalProperties": false,
120
+ "properties": {
121
+ "scopes": {
122
+ "type": "object",
123
+ "additionalProperties": {
124
+ "type": "object",
125
+ "required": [
126
+ "lineRate",
127
+ "branchRate",
128
+ "linesCovered",
129
+ "linesValid",
130
+ "floor",
131
+ "modules"
132
+ ],
133
+ "properties": {
134
+ "lineRate": {
135
+ "type": [
136
+ "number",
137
+ "null"
138
+ ],
139
+ "minimum": 0,
140
+ "maximum": 1
141
+ },
142
+ "branchRate": {
143
+ "type": [
144
+ "number",
145
+ "null"
146
+ ],
147
+ "minimum": 0,
148
+ "maximum": 1
149
+ },
150
+ "linesCovered": {
151
+ "type": "integer",
152
+ "minimum": 0
153
+ },
154
+ "linesValid": {
155
+ "type": "integer",
156
+ "minimum": 0
157
+ },
158
+ "floor": {
159
+ "type": [
160
+ "number",
161
+ "null"
162
+ ],
163
+ "minimum": 0,
164
+ "maximum": 1
165
+ },
166
+ "modules": {
167
+ "type": "object",
168
+ "additionalProperties": {
169
+ "type": "object",
170
+ "required": [
171
+ "lineRate",
172
+ "branchRate",
173
+ "linesCovered",
174
+ "linesValid"
175
+ ]
176
+ }
177
+ }
178
+ }
179
+ }
180
+ },
181
+ "global": {
182
+ "type": "object",
183
+ "required": [
184
+ "lineRate",
185
+ "linesCovered",
186
+ "linesValid"
187
+ ]
188
+ },
189
+ "passed": {
190
+ "type": "boolean"
191
+ }
192
+ }
193
+ },
194
+ "complexitySummary": {
195
+ "type": "object",
196
+ "required": [
197
+ "methods",
198
+ "ccP50",
199
+ "ccP90",
200
+ "ccMax",
201
+ "over10",
202
+ "crapOver30"
203
+ ],
204
+ "properties": {
205
+ "methods": {
206
+ "type": "integer",
207
+ "minimum": 0
208
+ },
209
+ "ccP50": {
210
+ "type": [
211
+ "number",
212
+ "null"
213
+ ]
214
+ },
215
+ "ccP90": {
216
+ "type": [
217
+ "number",
218
+ "null"
219
+ ]
220
+ },
221
+ "ccMax": {
222
+ "type": [
223
+ "number",
224
+ "null"
225
+ ]
226
+ },
227
+ "over10": {
228
+ "type": "integer",
229
+ "minimum": 0
230
+ },
231
+ "crapOver30": {
232
+ "type": "integer",
233
+ "minimum": 0
234
+ }
235
+ }
236
+ },
237
+ "complexity": {
238
+ "type": "object",
239
+ "required": [
240
+ "thresholds",
241
+ "scopes",
242
+ "global",
243
+ "hotspots"
244
+ ],
245
+ "additionalProperties": false,
246
+ "properties": {
247
+ "thresholds": {
248
+ "type": "object",
249
+ "required": [
250
+ "cc",
251
+ "crap"
252
+ ]
253
+ },
254
+ "scopes": {
255
+ "type": "object",
256
+ "additionalProperties": {
257
+ "type": "object",
258
+ "required": [
259
+ "methods",
260
+ "ccP50",
261
+ "ccP90",
262
+ "ccMax",
263
+ "over10",
264
+ "crapOver30",
265
+ "modules"
266
+ ]
267
+ }
268
+ },
269
+ "global": {
270
+ "type": "object",
271
+ "required": [
272
+ "methods",
273
+ "ccP50",
274
+ "ccP90",
275
+ "ccMax",
276
+ "over10",
277
+ "crapOver30"
278
+ ]
279
+ },
280
+ "hotspots": {
281
+ "type": "array",
282
+ "items": {
283
+ "type": "object",
284
+ "required": [
285
+ "scope",
286
+ "module",
287
+ "file",
288
+ "method",
289
+ "line",
290
+ "cc",
291
+ "coverage",
292
+ "crap"
293
+ ],
294
+ "properties": {
295
+ "scope": {
296
+ "type": "string"
297
+ },
298
+ "module": {
299
+ "type": "string"
300
+ },
301
+ "file": {
302
+ "type": "string"
303
+ },
304
+ "method": {
305
+ "type": "string"
306
+ },
307
+ "line": {
308
+ "type": [
309
+ "integer",
310
+ "null"
311
+ ]
312
+ },
313
+ "cc": {
314
+ "type": "number",
315
+ "minimum": 1
316
+ },
317
+ "coverage": {
318
+ "type": "number",
319
+ "minimum": 0,
320
+ "maximum": 1
321
+ },
322
+ "crap": {
323
+ "type": "number",
324
+ "minimum": 1
325
+ }
326
+ }
327
+ }
328
+ },
329
+ "gate": {
330
+ "type": [
331
+ "object",
332
+ "null"
333
+ ],
334
+ "description": "Blocking complexity gate (Atlas #1941) as evaluated on this commit: backlog size (baseline entries still violating), violations outside the baseline, and justified exemptions. Absent or null when the repo runs no gate.",
335
+ "required": [
336
+ "ccMax",
337
+ "crapMax",
338
+ "baselined",
339
+ "violations",
340
+ "justified"
341
+ ],
342
+ "properties": {
343
+ "ccMax": {
344
+ "type": "number"
345
+ },
346
+ "crapMax": {
347
+ "type": "number"
348
+ },
349
+ "baselined": {
350
+ "type": "integer",
351
+ "minimum": 0
352
+ },
353
+ "violations": {
354
+ "type": "integer",
355
+ "minimum": 0
356
+ },
357
+ "justified": {
358
+ "type": "integer",
359
+ "minimum": 0
360
+ }
361
+ }
362
+ }
363
+ }
364
+ },
365
+ "mutationScores": {
366
+ "type": "object",
367
+ "required": [
368
+ "coveredScore",
369
+ "fullScore",
370
+ "killed",
371
+ "timeout",
372
+ "survived",
373
+ "noCoverage",
374
+ "valid"
375
+ ],
376
+ "properties": {
377
+ "coveredScore": {
378
+ "type": "number",
379
+ "minimum": 0,
380
+ "maximum": 1
381
+ },
382
+ "fullScore": {
383
+ "type": "number",
384
+ "minimum": 0,
385
+ "maximum": 1
386
+ },
387
+ "killed": {
388
+ "type": "integer",
389
+ "minimum": 0
390
+ },
391
+ "timeout": {
392
+ "type": "integer",
393
+ "minimum": 0
394
+ },
395
+ "survived": {
396
+ "type": "integer",
397
+ "minimum": 0
398
+ },
399
+ "noCoverage": {
400
+ "type": "integer",
401
+ "minimum": 0
402
+ },
403
+ "valid": {
404
+ "type": "integer",
405
+ "minimum": 0
406
+ }
407
+ }
408
+ },
409
+ "mutation": {
410
+ "type": "object",
411
+ "required": [
412
+ "scopes",
413
+ "global",
414
+ "passed"
415
+ ],
416
+ "additionalProperties": false,
417
+ "properties": {
418
+ "scopes": {
419
+ "type": "object",
420
+ "additionalProperties": {
421
+ "type": "object",
422
+ "required": [
423
+ "coveredScore",
424
+ "fullScore",
425
+ "killed",
426
+ "timeout",
427
+ "survived",
428
+ "noCoverage",
429
+ "valid",
430
+ "floor",
431
+ "passed",
432
+ "modules"
433
+ ],
434
+ "properties": {
435
+ "coveredScore": {
436
+ "type": "number",
437
+ "minimum": 0,
438
+ "maximum": 1
439
+ },
440
+ "fullScore": {
441
+ "type": "number",
442
+ "minimum": 0,
443
+ "maximum": 1
444
+ },
445
+ "floor": {
446
+ "type": [
447
+ "number",
448
+ "null"
449
+ ],
450
+ "minimum": 0,
451
+ "maximum": 1
452
+ },
453
+ "passed": {
454
+ "type": "boolean"
455
+ },
456
+ "modules": {
457
+ "type": "object",
458
+ "additionalProperties": {
459
+ "type": "object",
460
+ "required": [
461
+ "coveredScore",
462
+ "fullScore",
463
+ "killed",
464
+ "timeout",
465
+ "survived",
466
+ "noCoverage",
467
+ "valid"
468
+ ]
469
+ }
470
+ }
471
+ }
472
+ }
473
+ },
474
+ "global": {
475
+ "type": "object",
476
+ "required": [
477
+ "coveredScore",
478
+ "fullScore",
479
+ "killed",
480
+ "timeout",
481
+ "survived",
482
+ "noCoverage",
483
+ "valid"
484
+ ]
485
+ },
486
+ "passed": {
487
+ "type": "boolean"
488
+ }
489
+ }
490
+ },
491
+ "severities": {
492
+ "type": "object",
493
+ "required": [
494
+ "CRITICAL",
495
+ "HIGH",
496
+ "MEDIUM",
497
+ "LOW",
498
+ "UNKNOWN"
499
+ ]
500
+ },
501
+ "security": {
502
+ "type": "object",
503
+ "required": [
504
+ "trivy",
505
+ "semgrep",
506
+ "jobs",
507
+ "passed"
508
+ ],
509
+ "additionalProperties": false,
510
+ "properties": {
511
+ "trivy": {
512
+ "type": [
513
+ "object",
514
+ "null"
515
+ ],
516
+ "required": [
517
+ "vuln",
518
+ "misconfig",
519
+ "secret",
520
+ "fixableVulns"
521
+ ],
522
+ "properties": {
523
+ "vuln": {
524
+ "type": "object",
525
+ "required": [
526
+ "CRITICAL",
527
+ "HIGH",
528
+ "MEDIUM",
529
+ "LOW",
530
+ "UNKNOWN"
531
+ ]
532
+ },
533
+ "misconfig": {
534
+ "type": "object",
535
+ "required": [
536
+ "CRITICAL",
537
+ "HIGH",
538
+ "MEDIUM",
539
+ "LOW",
540
+ "UNKNOWN"
541
+ ]
542
+ },
543
+ "secret": {
544
+ "type": "object",
545
+ "required": [
546
+ "CRITICAL",
547
+ "HIGH",
548
+ "MEDIUM",
549
+ "LOW",
550
+ "UNKNOWN"
551
+ ]
552
+ },
553
+ "fixableVulns": {
554
+ "type": "integer",
555
+ "minimum": 0
556
+ }
557
+ }
558
+ },
559
+ "semgrep": {
560
+ "type": [
561
+ "object",
562
+ "null"
563
+ ],
564
+ "required": [
565
+ "ERROR",
566
+ "WARNING",
567
+ "INFO",
568
+ "total",
569
+ "scanErrors"
570
+ ]
571
+ },
572
+ "jobs": {
573
+ "type": "object",
574
+ "additionalProperties": {
575
+ "type": "string",
576
+ "enum": [
577
+ "success",
578
+ "failure",
579
+ "cancelled",
580
+ "skipped"
581
+ ]
582
+ }
583
+ },
584
+ "passed": {
585
+ "type": "boolean"
586
+ }
587
+ }
588
+ },
589
+ "sbom": {
590
+ "type": "object",
591
+ "required": [
592
+ "format",
593
+ "specVersion",
594
+ "components",
595
+ "byEcosystem",
596
+ "licenses",
597
+ "unlicensed",
598
+ "sha256"
599
+ ],
600
+ "additionalProperties": false,
601
+ "properties": {
602
+ "format": {
603
+ "type": "string"
604
+ },
605
+ "specVersion": {
606
+ "type": [
607
+ "string",
608
+ "null"
609
+ ]
610
+ },
611
+ "components": {
612
+ "type": "integer",
613
+ "minimum": 0
614
+ },
615
+ "byEcosystem": {
616
+ "type": "object",
617
+ "additionalProperties": {
618
+ "type": "integer",
619
+ "minimum": 0
620
+ }
621
+ },
622
+ "licenses": {
623
+ "type": "object",
624
+ "additionalProperties": {
625
+ "type": "integer",
626
+ "minimum": 0
627
+ }
628
+ },
629
+ "unlicensed": {
630
+ "type": "integer",
631
+ "minimum": 0
632
+ },
633
+ "sha256": {
634
+ "type": "string"
635
+ }
636
+ }
637
+ },
638
+ "hygieneSummary": {
639
+ "type": "object",
640
+ "required": [
641
+ "files",
642
+ "lines",
643
+ "duplicatedLines",
644
+ "duplicationRate",
645
+ "codeLines",
646
+ "commentLines",
647
+ "commentDensity",
648
+ "swallowedExceptions",
649
+ "justifiedSwallows"
650
+ ],
651
+ "additionalProperties": false,
652
+ "properties": {
653
+ "files": {
654
+ "$ref": "#/$defs/count"
655
+ },
656
+ "lines": {
657
+ "$ref": "#/$defs/count",
658
+ "description": "Physical lines of the scanned files."
659
+ },
660
+ "duplicatedLines": {
661
+ "$ref": "#/$defs/count",
662
+ "description": "Lines inside at least one jscpd clone, each line counted once per file; both fragments of a clone count."
663
+ },
664
+ "duplicationRate": {
665
+ "$ref": "#/$defs/rate",
666
+ "description": "duplicatedLines / lines."
667
+ },
668
+ "codeLines": {
669
+ "$ref": "#/$defs/count",
670
+ "description": "Non-blank lines that hold code."
671
+ },
672
+ "commentLines": {
673
+ "$ref": "#/$defs/count",
674
+ "description": "Lines that hold only a comment."
675
+ },
676
+ "commentDensity": {
677
+ "type": [
678
+ "number",
679
+ "null"
680
+ ],
681
+ "minimum": 0,
682
+ "description": "commentLines / codeLines. Report-only: a line-based approximation for a trend, never a gate."
683
+ },
684
+ "swallowedExceptions": {
685
+ "type": [
686
+ "integer",
687
+ "null"
688
+ ],
689
+ "minimum": 0,
690
+ "description": "Unjustified empty catch blocks (Semgrep hygiene rules). Null when no Semgrep result was available. Blocking in Atlas, so normally 0 on main."
691
+ },
692
+ "justifiedSwallows": {
693
+ "$ref": "#/$defs/count",
694
+ "description": "Empty catch blocks kept with a `nosemgrep: <hygiene rule> — <reason>` justification."
695
+ }
696
+ }
697
+ },
698
+ "hygieneScope": {
699
+ "type": "object",
700
+ "required": [
701
+ "files",
702
+ "lines",
703
+ "duplicatedLines",
704
+ "duplicationRate",
705
+ "codeLines",
706
+ "commentLines",
707
+ "commentDensity",
708
+ "swallowedExceptions",
709
+ "justifiedSwallows",
710
+ "deadCodeWarnings",
711
+ "modules"
712
+ ],
713
+ "additionalProperties": false,
714
+ "properties": {
715
+ "files": {
716
+ "$ref": "#/$defs/count"
717
+ },
718
+ "lines": {
719
+ "$ref": "#/$defs/count",
720
+ "description": "Physical lines of the scanned files."
721
+ },
722
+ "duplicatedLines": {
723
+ "$ref": "#/$defs/count",
724
+ "description": "Lines inside at least one jscpd clone, each line counted once per file; both fragments of a clone count."
725
+ },
726
+ "duplicationRate": {
727
+ "$ref": "#/$defs/rate",
728
+ "description": "duplicatedLines / lines."
729
+ },
730
+ "codeLines": {
731
+ "$ref": "#/$defs/count",
732
+ "description": "Non-blank lines that hold code."
733
+ },
734
+ "commentLines": {
735
+ "$ref": "#/$defs/count",
736
+ "description": "Lines that hold only a comment."
737
+ },
738
+ "commentDensity": {
739
+ "type": [
740
+ "number",
741
+ "null"
742
+ ],
743
+ "minimum": 0,
744
+ "description": "commentLines / codeLines. Report-only: a line-based approximation for a trend, never a gate."
745
+ },
746
+ "swallowedExceptions": {
747
+ "type": [
748
+ "integer",
749
+ "null"
750
+ ],
751
+ "minimum": 0,
752
+ "description": "Unjustified empty catch blocks (Semgrep hygiene rules). Null when no Semgrep result was available. Blocking in Atlas, so normally 0 on main."
753
+ },
754
+ "justifiedSwallows": {
755
+ "$ref": "#/$defs/count",
756
+ "description": "Empty catch blocks kept with a `nosemgrep: <hygiene rule> — <reason>` justification."
757
+ },
758
+ "deadCodeWarnings": {
759
+ "$ref": "#/$defs/count",
760
+ "description": "Dead-code diagnostics left in the scope (.NET IDE0051/IDE0052, TS noUnusedLocals/noUnusedParameters). 0 while they are build errors."
761
+ },
762
+ "modules": {
763
+ "type": "object",
764
+ "additionalProperties": {
765
+ "$ref": "#/$defs/hygieneSummary"
766
+ }
767
+ }
768
+ }
769
+ },
770
+ "hygiene": {
771
+ "type": "object",
772
+ "description": "AI-slop proxies (Atlas #1942): duplication, swallowed exceptions, dead code and comment density, per scope (api, web) and module. Scope 'api' is apps/api/src, 'web' apps/web/src; tests, generated code and migrations are excluded.",
773
+ "required": [
774
+ "thresholds",
775
+ "scopes",
776
+ "global",
777
+ "deadCode",
778
+ "hotspots",
779
+ "gate"
780
+ ],
781
+ "additionalProperties": false,
782
+ "properties": {
783
+ "thresholds": {
784
+ "type": "object",
785
+ "description": "The jscpd clone minimums: a clone must reach both.",
786
+ "required": [
787
+ "minLines",
788
+ "minTokens"
789
+ ],
790
+ "additionalProperties": false,
791
+ "properties": {
792
+ "minLines": {
793
+ "type": "integer",
794
+ "minimum": 1
795
+ },
796
+ "minTokens": {
797
+ "type": "integer",
798
+ "minimum": 1
799
+ }
800
+ }
801
+ },
802
+ "scopes": {
803
+ "type": "object",
804
+ "additionalProperties": {
805
+ "$ref": "#/$defs/hygieneScope"
806
+ }
807
+ },
808
+ "global": {
809
+ "$ref": "#/$defs/hygieneSummary"
810
+ },
811
+ "deadCode": {
812
+ "type": "object",
813
+ "required": [
814
+ "gated",
815
+ "warnings"
816
+ ],
817
+ "additionalProperties": false,
818
+ "properties": {
819
+ "gated": {
820
+ "type": "boolean",
821
+ "description": "True when dead code fails the build, so warnings stays 0."
822
+ },
823
+ "warnings": {
824
+ "$ref": "#/$defs/count"
825
+ }
826
+ }
827
+ },
828
+ "hotspots": {
829
+ "type": "array",
830
+ "description": "Files with the most duplicated lines, worst first, at most 20.",
831
+ "items": {
832
+ "type": "object",
833
+ "required": [
834
+ "scope",
835
+ "module",
836
+ "file",
837
+ "duplicatedLines",
838
+ "lines",
839
+ "duplicationRate"
840
+ ],
841
+ "additionalProperties": false,
842
+ "properties": {
843
+ "scope": {
844
+ "type": "string"
845
+ },
846
+ "module": {
847
+ "type": "string"
848
+ },
849
+ "file": {
850
+ "type": "string"
851
+ },
852
+ "duplicatedLines": {
853
+ "$ref": "#/$defs/count"
854
+ },
855
+ "lines": {
856
+ "$ref": "#/$defs/count"
857
+ },
858
+ "duplicationRate": {
859
+ "$ref": "#/$defs/rate"
860
+ }
861
+ }
862
+ }
863
+ },
864
+ "gate": {
865
+ "type": [
866
+ "object",
867
+ "null"
868
+ ],
869
+ "description": "Blocking duplication gate as evaluated on this commit: files in the baseline, their recorded duplicated lines, files over their baseline (or new clones), and baseline entries that improved. Null when the repo runs no gate.",
870
+ "required": [
871
+ "baselinedFiles",
872
+ "baselineLines",
873
+ "violations",
874
+ "fixed"
875
+ ],
876
+ "additionalProperties": false,
877
+ "properties": {
878
+ "baselinedFiles": {
879
+ "$ref": "#/$defs/count"
880
+ },
881
+ "baselineLines": {
882
+ "$ref": "#/$defs/count"
883
+ },
884
+ "violations": {
885
+ "$ref": "#/$defs/count"
886
+ },
887
+ "fixed": {
888
+ "$ref": "#/$defs/count"
889
+ }
890
+ }
891
+ }
892
+ }
893
+ }
894
+ }
895
+ }