@mmerterden/multi-agent-pipeline 20.8.3 → 20.9.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/CHANGELOG.md +37 -0
  2. package/docs/facts.json +1 -1
  3. package/install/claude.mjs +1 -1
  4. package/manifest.json +37 -28
  5. package/package.json +1 -1
  6. package/pipeline/lib/claude-md-links.mjs +328 -0
  7. package/pipeline/lib/owned-path-gate.mjs +699 -0
  8. package/pipeline/lib/repo-profile-derive.mjs +1771 -0
  9. package/pipeline/lib/repo-profile.mjs +780 -0
  10. package/pipeline/lib/stack-detect.sh +59 -19
  11. package/pipeline/lib/unattended.mjs +17 -0
  12. package/pipeline/multi-agent-refs/features/repo-profile.md +96 -0
  13. package/pipeline/multi-agent-refs/features/review-decision.md +18 -13
  14. package/pipeline/multi-agent-refs/features/stack-skill-routing.md +179 -33
  15. package/pipeline/multi-agent-refs/outside-the-pipeline.md +33 -11
  16. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +26 -12
  17. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +24 -13
  18. package/pipeline/multi-agent-refs/phases/phase-3-review.md +16 -4
  19. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +1 -1
  20. package/pipeline/multi-agent-refs/phases/phase-5-report.md +8 -0
  21. package/pipeline/rules/outside-the-pipeline.md +6 -1
  22. package/pipeline/schemas/agent-state.schema.json +66 -2
  23. package/pipeline/schemas/phases.json +4 -4
  24. package/pipeline/schemas/repo-profile.schema.json +1107 -0
  25. package/pipeline/schemas/token-budget.json +4 -4
  26. package/pipeline/scripts/agent-guard.py +30 -0
  27. package/pipeline/scripts/owned-path-gate.mjs +205 -0
  28. package/pipeline/scripts/pre-commit-check.sh +151 -1
  29. package/pipeline/scripts/repo-profile.mjs +244 -0
  30. package/pipeline/scripts/review-decision-gate.mjs +42 -18
  31. package/pipeline/scripts/skill-candidates.mjs +882 -0
  32. package/pipeline/scripts/unattended_policy.py +90 -0
  33. package/pipeline/scripts/usage-report.mjs +36 -6
  34. package/pipeline/skills/.skill-manifest.json +1 -1
@@ -0,0 +1,1107 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/mmerterden/multi-agent-pipeline/pipeline/schemas/repo-profile.schema.json",
4
+ "title": "Repo Profile",
5
+ "description": "Per-repo project profile: the conventions, owned paths, generators, commands and hooks a generic skill needs to act correctly in one specific repo. Derived from the repo itself by pipeline/scripts/repo-profile.mjs, confirmed once by the user, and stored at ~/.claude/projects/{slug}/repo-profile.json with mode 0600. Never written inside the repo and never committed. Every role carries its value, where it came from (derived | confirmed | manual), the evidence behind it (file:line, commit:<sha> or git:<ref>) and a confidence. A null value means the role could not be derived; consumers treat it as absent and keep their default.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "schemaVersion",
10
+ "repoRoot",
11
+ "repoHead",
12
+ "derivedAt",
13
+ "confirmedAt",
14
+ "repo",
15
+ "commit",
16
+ "build",
17
+ "test",
18
+ "lint",
19
+ "ownedPaths",
20
+ "generators",
21
+ "resourceSource",
22
+ "accessors",
23
+ "di",
24
+ "module",
25
+ "mock",
26
+ "hooks",
27
+ "requiredChecks",
28
+ "docs",
29
+ "inRepoSkills"
30
+ ],
31
+ "properties": {
32
+ "schemaVersion": {
33
+ "type": "string",
34
+ "const": "1.0.0"
35
+ },
36
+ "repoRoot": {
37
+ "type": "string",
38
+ "minLength": 1,
39
+ "description": "Absolute path of the main checkout the profile describes. Worktrees of the same repo share it."
40
+ },
41
+ "repoHead": {
42
+ "anyOf": [
43
+ {
44
+ "type": "string",
45
+ "pattern": "^[0-9a-f]{40}$"
46
+ },
47
+ {
48
+ "type": "null"
49
+ }
50
+ ]
51
+ },
52
+ "derivedAt": {
53
+ "type": "string",
54
+ "format": "date-time"
55
+ },
56
+ "confirmedAt": {
57
+ "anyOf": [
58
+ {
59
+ "type": "string",
60
+ "format": "date-time"
61
+ },
62
+ {
63
+ "type": "null"
64
+ }
65
+ ]
66
+ },
67
+ "derivation": {
68
+ "type": "object",
69
+ "additionalProperties": false,
70
+ "description": "How the draft was produced, so a re-derive can use the same settings.",
71
+ "properties": {
72
+ "botPattern": {
73
+ "type": "string"
74
+ },
75
+ "historyWindow": {
76
+ "type": "integer",
77
+ "minimum": 1
78
+ },
79
+ "durationMs": {
80
+ "type": "integer",
81
+ "minimum": 0
82
+ },
83
+ "conventions": {
84
+ "type": "boolean",
85
+ "description": "True when an extract-conventions.sh report was folded in."
86
+ }
87
+ }
88
+ },
89
+ "repo": {
90
+ "type": "object",
91
+ "additionalProperties": false,
92
+ "required": ["workBranch", "defaultBranch"],
93
+ "properties": {
94
+ "workBranch": {
95
+ "type": "object",
96
+ "description": "Branch feature work targets: pull_request branch filters in CI, else the most active integration branch.",
97
+ "additionalProperties": false,
98
+ "required": ["value", "source", "evidence", "confidence"],
99
+ "properties": {
100
+ "value": {
101
+ "anyOf": [
102
+ {
103
+ "type": "string",
104
+ "minLength": 1
105
+ },
106
+ {
107
+ "type": "null"
108
+ }
109
+ ]
110
+ },
111
+ "source": {
112
+ "$ref": "#/$defs/source"
113
+ },
114
+ "evidence": {
115
+ "$ref": "#/$defs/evidence"
116
+ },
117
+ "confidence": {
118
+ "$ref": "#/$defs/confidence"
119
+ },
120
+ "note": {
121
+ "type": "string"
122
+ }
123
+ }
124
+ },
125
+ "defaultBranch": {
126
+ "type": "object",
127
+ "description": "origin/HEAD.",
128
+ "additionalProperties": false,
129
+ "required": ["value", "source", "evidence", "confidence"],
130
+ "properties": {
131
+ "value": {
132
+ "anyOf": [
133
+ {
134
+ "type": "string",
135
+ "minLength": 1
136
+ },
137
+ {
138
+ "type": "null"
139
+ }
140
+ ]
141
+ },
142
+ "source": {
143
+ "$ref": "#/$defs/source"
144
+ },
145
+ "evidence": {
146
+ "$ref": "#/$defs/evidence"
147
+ },
148
+ "confidence": {
149
+ "$ref": "#/$defs/confidence"
150
+ },
151
+ "note": {
152
+ "type": "string"
153
+ }
154
+ }
155
+ }
156
+ }
157
+ },
158
+ "commit": {
159
+ "type": "object",
160
+ "additionalProperties": false,
161
+ "required": ["format"],
162
+ "properties": {
163
+ "format": {
164
+ "type": "object",
165
+ "description": "Commit subject convention from recent non-merge, non-bot commits.",
166
+ "additionalProperties": false,
167
+ "required": ["value", "source", "evidence", "confidence"],
168
+ "properties": {
169
+ "value": {
170
+ "anyOf": [
171
+ {
172
+ "type": "object",
173
+ "additionalProperties": false,
174
+ "required": [
175
+ "ticketPosition",
176
+ "ticketStyle",
177
+ "scopeRequired",
178
+ "conventional",
179
+ "example",
180
+ "sampleSize"
181
+ ],
182
+ "properties": {
183
+ "ticketPosition": {
184
+ "enum": ["prefix", "suffix", "none"]
185
+ },
186
+ "ticketStyle": {
187
+ "anyOf": [
188
+ {
189
+ "type": "string",
190
+ "minLength": 1
191
+ },
192
+ {
193
+ "type": "null"
194
+ }
195
+ ],
196
+ "description": "Template of the ticket token with KEY-123 as the placeholder, e.g. [KEY-123], (KEY-123), KEY-123:, #123."
197
+ },
198
+ "scopeRequired": {
199
+ "type": "boolean"
200
+ },
201
+ "conventional": {
202
+ "type": "boolean",
203
+ "description": "Majority of subjects start with type: or type(scope):."
204
+ },
205
+ "example": {
206
+ "anyOf": [
207
+ {
208
+ "type": "string",
209
+ "minLength": 1
210
+ },
211
+ {
212
+ "type": "null"
213
+ }
214
+ ]
215
+ },
216
+ "sampleSize": {
217
+ "type": "integer",
218
+ "minimum": 0
219
+ }
220
+ }
221
+ },
222
+ {
223
+ "type": "null"
224
+ }
225
+ ]
226
+ },
227
+ "source": {
228
+ "$ref": "#/$defs/source"
229
+ },
230
+ "evidence": {
231
+ "$ref": "#/$defs/evidence"
232
+ },
233
+ "confidence": {
234
+ "$ref": "#/$defs/confidence"
235
+ },
236
+ "note": {
237
+ "type": "string"
238
+ }
239
+ }
240
+ }
241
+ }
242
+ },
243
+ "build": {
244
+ "type": "object",
245
+ "description": "Build command, exact flags kept.",
246
+ "additionalProperties": false,
247
+ "required": ["value", "source", "evidence", "confidence"],
248
+ "properties": {
249
+ "value": {
250
+ "anyOf": [
251
+ {
252
+ "type": "object",
253
+ "additionalProperties": false,
254
+ "required": ["command", "source"],
255
+ "properties": {
256
+ "command": {
257
+ "type": "string",
258
+ "minLength": 1
259
+ },
260
+ "source": {
261
+ "type": "string",
262
+ "minLength": 1,
263
+ "description": "File the command was read from."
264
+ }
265
+ }
266
+ },
267
+ {
268
+ "type": "null"
269
+ }
270
+ ]
271
+ },
272
+ "source": {
273
+ "$ref": "#/$defs/source"
274
+ },
275
+ "evidence": {
276
+ "$ref": "#/$defs/evidence"
277
+ },
278
+ "confidence": {
279
+ "$ref": "#/$defs/confidence"
280
+ },
281
+ "note": {
282
+ "type": "string"
283
+ }
284
+ }
285
+ },
286
+ "test": {
287
+ "type": "object",
288
+ "description": "Test command and test plans.",
289
+ "additionalProperties": false,
290
+ "required": ["value", "source", "evidence", "confidence"],
291
+ "properties": {
292
+ "value": {
293
+ "anyOf": [
294
+ {
295
+ "type": "object",
296
+ "additionalProperties": false,
297
+ "required": ["command", "plans"],
298
+ "properties": {
299
+ "command": {
300
+ "anyOf": [
301
+ {
302
+ "type": "string",
303
+ "minLength": 1
304
+ },
305
+ {
306
+ "type": "null"
307
+ }
308
+ ]
309
+ },
310
+ "plans": {
311
+ "type": "array",
312
+ "items": {
313
+ "type": "string",
314
+ "minLength": 1
315
+ }
316
+ }
317
+ }
318
+ },
319
+ {
320
+ "type": "null"
321
+ }
322
+ ]
323
+ },
324
+ "source": {
325
+ "$ref": "#/$defs/source"
326
+ },
327
+ "evidence": {
328
+ "$ref": "#/$defs/evidence"
329
+ },
330
+ "confidence": {
331
+ "$ref": "#/$defs/confidence"
332
+ },
333
+ "note": {
334
+ "type": "string"
335
+ }
336
+ }
337
+ },
338
+ "lint": {
339
+ "type": "object",
340
+ "description": "Lint command.",
341
+ "additionalProperties": false,
342
+ "required": ["value", "source", "evidence", "confidence"],
343
+ "properties": {
344
+ "value": {
345
+ "anyOf": [
346
+ {
347
+ "type": "object",
348
+ "additionalProperties": false,
349
+ "required": ["command"],
350
+ "properties": {
351
+ "command": {
352
+ "type": "string",
353
+ "minLength": 1
354
+ }
355
+ }
356
+ },
357
+ {
358
+ "type": "null"
359
+ }
360
+ ]
361
+ },
362
+ "source": {
363
+ "$ref": "#/$defs/source"
364
+ },
365
+ "evidence": {
366
+ "$ref": "#/$defs/evidence"
367
+ },
368
+ "confidence": {
369
+ "$ref": "#/$defs/confidence"
370
+ },
371
+ "note": {
372
+ "type": "string"
373
+ }
374
+ }
375
+ },
376
+ "ownedPaths": {
377
+ "type": "array",
378
+ "description": "Paths an automated account owns. Hand edits there are reverted or rejected.",
379
+ "items": {
380
+ "type": "object",
381
+ "additionalProperties": false,
382
+ "required": ["glob", "owner", "source", "evidence", "confidence"],
383
+ "properties": {
384
+ "glob": {
385
+ "type": "string",
386
+ "minLength": 1
387
+ },
388
+ "owner": {
389
+ "type": "string",
390
+ "minLength": 1,
391
+ "description": "The author(s) that own the path: a regular expression when it starts with ^ or ends with $, otherwise one literal name or e-mail compared exactly (a name such as sync[bot] needs no escaping).",
392
+ "format": "regex"
393
+ },
394
+ "bypass": {
395
+ "anyOf": [
396
+ {
397
+ "type": "object",
398
+ "additionalProperties": false,
399
+ "minProperties": 1,
400
+ "properties": {
401
+ "label": {
402
+ "type": "string",
403
+ "minLength": 1
404
+ },
405
+ "author": {
406
+ "type": "string",
407
+ "minLength": 1
408
+ }
409
+ }
410
+ },
411
+ {
412
+ "type": "null"
413
+ }
414
+ ]
415
+ },
416
+ "commits": {
417
+ "type": "integer",
418
+ "minimum": 0
419
+ },
420
+ "basis": {
421
+ "enum": ["history", "workflow", "history+workflow"],
422
+ "description": "What the rule rests on: commit history alone, a CI workflow that enforces it, or both. A history-only rule is at most medium and an attended run honours it only once confirmed."
423
+ },
424
+ "source": {
425
+ "$ref": "#/$defs/source"
426
+ },
427
+ "evidence": {
428
+ "$ref": "#/$defs/evidence"
429
+ },
430
+ "confidence": {
431
+ "$ref": "#/$defs/confidence"
432
+ },
433
+ "note": {
434
+ "type": "string"
435
+ },
436
+ "except": {
437
+ "type": "array",
438
+ "description": "Globs under this path that stay open to hand edits (a case arm or filter the owning job exempts).",
439
+ "items": {
440
+ "type": "string",
441
+ "minLength": 1
442
+ },
443
+ "uniqueItems": true
444
+ }
445
+ }
446
+ }
447
+ },
448
+ "generators": {
449
+ "type": "array",
450
+ "description": "Generated trees: output glob, the input that feeds it, the regeneration command, and whether input and output change in the same commit.",
451
+ "items": {
452
+ "type": "object",
453
+ "additionalProperties": false,
454
+ "required": [
455
+ "output",
456
+ "input",
457
+ "command",
458
+ "sameCommit",
459
+ "source",
460
+ "evidence",
461
+ "confidence"
462
+ ],
463
+ "properties": {
464
+ "output": {
465
+ "type": "string",
466
+ "minLength": 1
467
+ },
468
+ "input": {
469
+ "anyOf": [
470
+ {
471
+ "type": "string",
472
+ "minLength": 1
473
+ },
474
+ {
475
+ "type": "null"
476
+ }
477
+ ]
478
+ },
479
+ "command": {
480
+ "anyOf": [
481
+ {
482
+ "type": "string",
483
+ "minLength": 1
484
+ },
485
+ {
486
+ "type": "null"
487
+ }
488
+ ]
489
+ },
490
+ "check": {
491
+ "anyOf": [
492
+ {
493
+ "type": "string",
494
+ "minLength": 1
495
+ },
496
+ {
497
+ "type": "null"
498
+ }
499
+ ],
500
+ "description": "A CI step that only verifies the output is current (--check, --verify, --dry-run); running it regenerates nothing."
501
+ },
502
+ "sameCommit": {
503
+ "anyOf": [
504
+ {
505
+ "type": "boolean"
506
+ },
507
+ {
508
+ "type": "null"
509
+ }
510
+ ]
511
+ },
512
+ "stamp": {
513
+ "anyOf": [
514
+ {
515
+ "type": "string",
516
+ "minLength": 1
517
+ },
518
+ {
519
+ "type": "null"
520
+ }
521
+ ]
522
+ },
523
+ "source": {
524
+ "$ref": "#/$defs/source"
525
+ },
526
+ "evidence": {
527
+ "$ref": "#/$defs/evidence"
528
+ },
529
+ "confidence": {
530
+ "$ref": "#/$defs/confidence"
531
+ },
532
+ "note": {
533
+ "type": "string"
534
+ }
535
+ }
536
+ }
537
+ },
538
+ "resourceSource": {
539
+ "type": "object",
540
+ "description": "Where synced resources are authored, and the per-domain command to author them.",
541
+ "additionalProperties": false,
542
+ "required": ["value", "source", "evidence", "confidence"],
543
+ "properties": {
544
+ "value": {
545
+ "anyOf": [
546
+ {
547
+ "type": "object",
548
+ "additionalProperties": false,
549
+ "required": ["repo", "authoring"],
550
+ "properties": {
551
+ "repo": {
552
+ "anyOf": [
553
+ {
554
+ "type": "string",
555
+ "minLength": 1
556
+ },
557
+ {
558
+ "type": "null"
559
+ }
560
+ ]
561
+ },
562
+ "authoring": {
563
+ "type": "object",
564
+ "additionalProperties": {
565
+ "type": "string",
566
+ "minLength": 1
567
+ }
568
+ }
569
+ }
570
+ },
571
+ {
572
+ "type": "null"
573
+ }
574
+ ]
575
+ },
576
+ "source": {
577
+ "$ref": "#/$defs/source"
578
+ },
579
+ "evidence": {
580
+ "$ref": "#/$defs/evidence"
581
+ },
582
+ "confidence": {
583
+ "$ref": "#/$defs/confidence"
584
+ },
585
+ "note": {
586
+ "type": "string"
587
+ }
588
+ }
589
+ },
590
+ "accessors": {
591
+ "type": "object",
592
+ "additionalProperties": false,
593
+ "required": ["localization", "accessibility", "testingId", "tokens"],
594
+ "properties": {
595
+ "localization": {
596
+ "type": "object",
597
+ "description": "Root type of the localization accessor, e.g. the <Root> in <Root>.<Seg>.<Seg>.",
598
+ "additionalProperties": false,
599
+ "required": ["value", "source", "evidence", "confidence"],
600
+ "properties": {
601
+ "value": {
602
+ "anyOf": [
603
+ {
604
+ "type": "string",
605
+ "minLength": 1
606
+ },
607
+ {
608
+ "type": "null"
609
+ }
610
+ ]
611
+ },
612
+ "source": {
613
+ "$ref": "#/$defs/source"
614
+ },
615
+ "evidence": {
616
+ "$ref": "#/$defs/evidence"
617
+ },
618
+ "confidence": {
619
+ "$ref": "#/$defs/confidence"
620
+ },
621
+ "note": {
622
+ "type": "string"
623
+ }
624
+ }
625
+ },
626
+ "accessibility": {
627
+ "type": "object",
628
+ "description": "Accessibility identifier convention.",
629
+ "additionalProperties": false,
630
+ "required": ["value", "source", "evidence", "confidence"],
631
+ "properties": {
632
+ "value": {
633
+ "anyOf": [
634
+ {
635
+ "type": "string",
636
+ "minLength": 1
637
+ },
638
+ {
639
+ "type": "null"
640
+ }
641
+ ]
642
+ },
643
+ "source": {
644
+ "$ref": "#/$defs/source"
645
+ },
646
+ "evidence": {
647
+ "$ref": "#/$defs/evidence"
648
+ },
649
+ "confidence": {
650
+ "$ref": "#/$defs/confidence"
651
+ },
652
+ "note": {
653
+ "type": "string"
654
+ }
655
+ }
656
+ },
657
+ "testingId": {
658
+ "type": "object",
659
+ "description": "Root type of the testing identifier accessor.",
660
+ "additionalProperties": false,
661
+ "required": ["value", "source", "evidence", "confidence"],
662
+ "properties": {
663
+ "value": {
664
+ "anyOf": [
665
+ {
666
+ "type": "string",
667
+ "minLength": 1
668
+ },
669
+ {
670
+ "type": "null"
671
+ }
672
+ ]
673
+ },
674
+ "source": {
675
+ "$ref": "#/$defs/source"
676
+ },
677
+ "evidence": {
678
+ "$ref": "#/$defs/evidence"
679
+ },
680
+ "confidence": {
681
+ "$ref": "#/$defs/confidence"
682
+ },
683
+ "note": {
684
+ "type": "string"
685
+ }
686
+ }
687
+ },
688
+ "tokens": {
689
+ "type": "object",
690
+ "description": "Root types of design token accessors.",
691
+ "additionalProperties": false,
692
+ "required": ["value", "source", "evidence", "confidence"],
693
+ "properties": {
694
+ "value": {
695
+ "type": "array",
696
+ "items": {
697
+ "type": "string",
698
+ "minLength": 1
699
+ }
700
+ },
701
+ "source": {
702
+ "$ref": "#/$defs/source"
703
+ },
704
+ "evidence": {
705
+ "$ref": "#/$defs/evidence"
706
+ },
707
+ "confidence": {
708
+ "$ref": "#/$defs/confidence"
709
+ },
710
+ "note": {
711
+ "type": "string"
712
+ }
713
+ }
714
+ }
715
+ }
716
+ },
717
+ "di": {
718
+ "type": "object",
719
+ "additionalProperties": false,
720
+ "required": ["registrarSuffix", "registerMethod", "injectAttribute"],
721
+ "properties": {
722
+ "registrarSuffix": {
723
+ "type": "object",
724
+ "description": "File/type suffix of per-module dependency registrars.",
725
+ "additionalProperties": false,
726
+ "required": ["value", "source", "evidence", "confidence"],
727
+ "properties": {
728
+ "value": {
729
+ "anyOf": [
730
+ {
731
+ "type": "string",
732
+ "minLength": 1
733
+ },
734
+ {
735
+ "type": "null"
736
+ }
737
+ ]
738
+ },
739
+ "source": {
740
+ "$ref": "#/$defs/source"
741
+ },
742
+ "evidence": {
743
+ "$ref": "#/$defs/evidence"
744
+ },
745
+ "confidence": {
746
+ "$ref": "#/$defs/confidence"
747
+ },
748
+ "note": {
749
+ "type": "string"
750
+ }
751
+ }
752
+ },
753
+ "registerMethod": {
754
+ "type": "object",
755
+ "description": "Method a registrar implements.",
756
+ "additionalProperties": false,
757
+ "required": ["value", "source", "evidence", "confidence"],
758
+ "properties": {
759
+ "value": {
760
+ "anyOf": [
761
+ {
762
+ "type": "string",
763
+ "minLength": 1
764
+ },
765
+ {
766
+ "type": "null"
767
+ }
768
+ ]
769
+ },
770
+ "source": {
771
+ "$ref": "#/$defs/source"
772
+ },
773
+ "evidence": {
774
+ "$ref": "#/$defs/evidence"
775
+ },
776
+ "confidence": {
777
+ "$ref": "#/$defs/confidence"
778
+ },
779
+ "note": {
780
+ "type": "string"
781
+ }
782
+ }
783
+ },
784
+ "injectAttribute": {
785
+ "type": "object",
786
+ "description": "Property wrapper or annotation used for injection.",
787
+ "additionalProperties": false,
788
+ "required": ["value", "source", "evidence", "confidence"],
789
+ "properties": {
790
+ "value": {
791
+ "anyOf": [
792
+ {
793
+ "type": "string",
794
+ "minLength": 1
795
+ },
796
+ {
797
+ "type": "null"
798
+ }
799
+ ]
800
+ },
801
+ "source": {
802
+ "$ref": "#/$defs/source"
803
+ },
804
+ "evidence": {
805
+ "$ref": "#/$defs/evidence"
806
+ },
807
+ "confidence": {
808
+ "$ref": "#/$defs/confidence"
809
+ },
810
+ "note": {
811
+ "type": "string"
812
+ }
813
+ }
814
+ }
815
+ }
816
+ },
817
+ "module": {
818
+ "type": "object",
819
+ "additionalProperties": false,
820
+ "required": ["layout", "validator"],
821
+ "properties": {
822
+ "layout": {
823
+ "type": "object",
824
+ "description": "Glob of repeated module manifests.",
825
+ "additionalProperties": false,
826
+ "required": ["value", "source", "evidence", "confidence"],
827
+ "properties": {
828
+ "value": {
829
+ "anyOf": [
830
+ {
831
+ "type": "string",
832
+ "minLength": 1
833
+ },
834
+ {
835
+ "type": "null"
836
+ }
837
+ ]
838
+ },
839
+ "source": {
840
+ "$ref": "#/$defs/source"
841
+ },
842
+ "evidence": {
843
+ "$ref": "#/$defs/evidence"
844
+ },
845
+ "confidence": {
846
+ "$ref": "#/$defs/confidence"
847
+ },
848
+ "note": {
849
+ "type": "string"
850
+ }
851
+ }
852
+ },
853
+ "validator": {
854
+ "type": "object",
855
+ "description": "Script or command that validates module structure.",
856
+ "additionalProperties": false,
857
+ "required": ["value", "source", "evidence", "confidence"],
858
+ "properties": {
859
+ "value": {
860
+ "anyOf": [
861
+ {
862
+ "type": "string",
863
+ "minLength": 1
864
+ },
865
+ {
866
+ "type": "null"
867
+ }
868
+ ]
869
+ },
870
+ "source": {
871
+ "$ref": "#/$defs/source"
872
+ },
873
+ "evidence": {
874
+ "$ref": "#/$defs/evidence"
875
+ },
876
+ "confidence": {
877
+ "$ref": "#/$defs/confidence"
878
+ },
879
+ "note": {
880
+ "type": "string"
881
+ }
882
+ }
883
+ }
884
+ }
885
+ },
886
+ "mock": {
887
+ "type": "object",
888
+ "additionalProperties": false,
889
+ "required": ["system", "customDir"],
890
+ "properties": {
891
+ "system": {
892
+ "type": "object",
893
+ "description": "Mocking approach: a framework name or fixture-tree.",
894
+ "additionalProperties": false,
895
+ "required": ["value", "source", "evidence", "confidence"],
896
+ "properties": {
897
+ "value": {
898
+ "anyOf": [
899
+ {
900
+ "type": "string",
901
+ "minLength": 1
902
+ },
903
+ {
904
+ "type": "null"
905
+ }
906
+ ]
907
+ },
908
+ "source": {
909
+ "$ref": "#/$defs/source"
910
+ },
911
+ "evidence": {
912
+ "$ref": "#/$defs/evidence"
913
+ },
914
+ "confidence": {
915
+ "$ref": "#/$defs/confidence"
916
+ },
917
+ "note": {
918
+ "type": "string"
919
+ }
920
+ }
921
+ },
922
+ "customDir": {
923
+ "type": "object",
924
+ "description": "Hand-authored mock/fixture tree that sits beside a generated one.",
925
+ "additionalProperties": false,
926
+ "required": ["value", "source", "evidence", "confidence"],
927
+ "properties": {
928
+ "value": {
929
+ "anyOf": [
930
+ {
931
+ "type": "string",
932
+ "minLength": 1
933
+ },
934
+ {
935
+ "type": "null"
936
+ }
937
+ ]
938
+ },
939
+ "source": {
940
+ "$ref": "#/$defs/source"
941
+ },
942
+ "evidence": {
943
+ "$ref": "#/$defs/evidence"
944
+ },
945
+ "confidence": {
946
+ "$ref": "#/$defs/confidence"
947
+ },
948
+ "note": {
949
+ "type": "string"
950
+ }
951
+ }
952
+ }
953
+ }
954
+ },
955
+ "hooks": {
956
+ "type": "array",
957
+ "description": "Git hooks the repo installs, and what they do to a commit.",
958
+ "items": {
959
+ "type": "object",
960
+ "additionalProperties": false,
961
+ "required": ["name", "path", "sideEffects", "source", "evidence", "confidence"],
962
+ "properties": {
963
+ "name": {
964
+ "type": "string",
965
+ "minLength": 1
966
+ },
967
+ "path": {
968
+ "type": "string",
969
+ "minLength": 1
970
+ },
971
+ "sideEffects": {
972
+ "type": "array",
973
+ "uniqueItems": true,
974
+ "items": {
975
+ "enum": ["amends-commit", "stages-files", "writes-files", "network"]
976
+ }
977
+ },
978
+ "source": {
979
+ "$ref": "#/$defs/source"
980
+ },
981
+ "evidence": {
982
+ "$ref": "#/$defs/evidence"
983
+ },
984
+ "confidence": {
985
+ "$ref": "#/$defs/confidence"
986
+ },
987
+ "note": {
988
+ "type": "string"
989
+ }
990
+ }
991
+ }
992
+ },
993
+ "requiredChecks": {
994
+ "type": "array",
995
+ "description": "CI checks the repo documents as required.",
996
+ "items": {
997
+ "type": "object",
998
+ "additionalProperties": false,
999
+ "required": ["name", "source", "evidence", "confidence"],
1000
+ "properties": {
1001
+ "name": {
1002
+ "type": "string",
1003
+ "minLength": 1
1004
+ },
1005
+ "source": {
1006
+ "$ref": "#/$defs/source"
1007
+ },
1008
+ "evidence": {
1009
+ "$ref": "#/$defs/evidence"
1010
+ },
1011
+ "confidence": {
1012
+ "$ref": "#/$defs/confidence"
1013
+ },
1014
+ "note": {
1015
+ "type": "string"
1016
+ }
1017
+ }
1018
+ }
1019
+ },
1020
+ "docs": {
1021
+ "type": "object",
1022
+ "additionalProperties": false,
1023
+ "required": ["authoritative"],
1024
+ "properties": {
1025
+ "authoritative": {
1026
+ "type": "array",
1027
+ "description": "Markdown files the repo's CLAUDE.md links to and that exist.",
1028
+ "items": {
1029
+ "type": "object",
1030
+ "additionalProperties": false,
1031
+ "required": ["path", "source", "evidence", "confidence"],
1032
+ "properties": {
1033
+ "path": {
1034
+ "type": "string",
1035
+ "minLength": 1
1036
+ },
1037
+ "source": {
1038
+ "$ref": "#/$defs/source"
1039
+ },
1040
+ "evidence": {
1041
+ "$ref": "#/$defs/evidence"
1042
+ },
1043
+ "confidence": {
1044
+ "$ref": "#/$defs/confidence"
1045
+ },
1046
+ "note": {
1047
+ "type": "string"
1048
+ }
1049
+ }
1050
+ }
1051
+ }
1052
+ }
1053
+ },
1054
+ "inRepoSkills": {
1055
+ "type": "array",
1056
+ "description": "Skills the repo ships under .claude/skills/.",
1057
+ "items": {
1058
+ "type": "object",
1059
+ "additionalProperties": false,
1060
+ "required": ["name", "path", "trigger", "source", "evidence", "confidence"],
1061
+ "properties": {
1062
+ "name": {
1063
+ "type": "string",
1064
+ "minLength": 1
1065
+ },
1066
+ "path": {
1067
+ "type": "string",
1068
+ "minLength": 1
1069
+ },
1070
+ "description": {
1071
+ "type": "string"
1072
+ },
1073
+ "trigger": {
1074
+ "enum": ["explicit", "auto"]
1075
+ },
1076
+ "source": {
1077
+ "$ref": "#/$defs/source"
1078
+ },
1079
+ "evidence": {
1080
+ "$ref": "#/$defs/evidence"
1081
+ },
1082
+ "confidence": {
1083
+ "$ref": "#/$defs/confidence"
1084
+ },
1085
+ "note": {
1086
+ "type": "string"
1087
+ }
1088
+ }
1089
+ }
1090
+ }
1091
+ },
1092
+ "$defs": {
1093
+ "source": {
1094
+ "enum": ["derived", "confirmed", "manual"]
1095
+ },
1096
+ "confidence": {
1097
+ "enum": ["high", "medium", "low"]
1098
+ },
1099
+ "evidence": {
1100
+ "type": "array",
1101
+ "items": {
1102
+ "type": "string",
1103
+ "pattern": "^(commit:[0-9a-f]{7,40}|git:\\S.*|[^:\\s][^:]*(:[0-9]+)?)$"
1104
+ }
1105
+ }
1106
+ }
1107
+ }