@aida-dev/metrics 0.4.0 → 0.5.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.
- package/dist/index.d.ts +505 -2
- package/dist/index.js +138 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Commit, CommitStream } from '@aida-dev/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
declare const Attribution: z.ZodObject<{
|
|
@@ -10,6 +10,38 @@ declare const Attribution: z.ZodObject<{
|
|
|
10
10
|
defaultAttribution: z.ZodEnum<["ai", "human", "unknown"]>;
|
|
11
11
|
coverageThreshold: z.ZodNumber;
|
|
12
12
|
belowThreshold: z.ZodBoolean;
|
|
13
|
+
modes: z.ZodObject<{
|
|
14
|
+
none: z.ZodNumber;
|
|
15
|
+
autocomplete: z.ZodNumber;
|
|
16
|
+
assisted: z.ZodNumber;
|
|
17
|
+
agent: z.ZodNumber;
|
|
18
|
+
unknown: z.ZodNumber;
|
|
19
|
+
}, "strip", z.ZodTypeAny, {
|
|
20
|
+
unknown: number;
|
|
21
|
+
none: number;
|
|
22
|
+
autocomplete: number;
|
|
23
|
+
assisted: number;
|
|
24
|
+
agent: number;
|
|
25
|
+
}, {
|
|
26
|
+
unknown: number;
|
|
27
|
+
none: number;
|
|
28
|
+
autocomplete: number;
|
|
29
|
+
assisted: number;
|
|
30
|
+
agent: number;
|
|
31
|
+
}>;
|
|
32
|
+
modeEvidence: z.ZodObject<{
|
|
33
|
+
declared: z.ZodNumber;
|
|
34
|
+
inferred: z.ZodNumber;
|
|
35
|
+
none: z.ZodNumber;
|
|
36
|
+
}, "strip", z.ZodTypeAny, {
|
|
37
|
+
none: number;
|
|
38
|
+
declared: number;
|
|
39
|
+
inferred: number;
|
|
40
|
+
}, {
|
|
41
|
+
none: number;
|
|
42
|
+
declared: number;
|
|
43
|
+
inferred: number;
|
|
44
|
+
}>;
|
|
13
45
|
}, "strip", z.ZodTypeAny, {
|
|
14
46
|
ai: number;
|
|
15
47
|
human: number;
|
|
@@ -19,6 +51,18 @@ declare const Attribution: z.ZodObject<{
|
|
|
19
51
|
defaultAttribution: "ai" | "human" | "unknown";
|
|
20
52
|
coverageThreshold: number;
|
|
21
53
|
belowThreshold: boolean;
|
|
54
|
+
modes: {
|
|
55
|
+
unknown: number;
|
|
56
|
+
none: number;
|
|
57
|
+
autocomplete: number;
|
|
58
|
+
assisted: number;
|
|
59
|
+
agent: number;
|
|
60
|
+
};
|
|
61
|
+
modeEvidence: {
|
|
62
|
+
none: number;
|
|
63
|
+
declared: number;
|
|
64
|
+
inferred: number;
|
|
65
|
+
};
|
|
22
66
|
}, {
|
|
23
67
|
ai: number;
|
|
24
68
|
human: number;
|
|
@@ -28,8 +72,125 @@ declare const Attribution: z.ZodObject<{
|
|
|
28
72
|
defaultAttribution: "ai" | "human" | "unknown";
|
|
29
73
|
coverageThreshold: number;
|
|
30
74
|
belowThreshold: boolean;
|
|
75
|
+
modes: {
|
|
76
|
+
unknown: number;
|
|
77
|
+
none: number;
|
|
78
|
+
autocomplete: number;
|
|
79
|
+
assisted: number;
|
|
80
|
+
agent: number;
|
|
81
|
+
};
|
|
82
|
+
modeEvidence: {
|
|
83
|
+
none: number;
|
|
84
|
+
declared: number;
|
|
85
|
+
inferred: number;
|
|
86
|
+
};
|
|
31
87
|
}>;
|
|
32
88
|
type Attribution = z.infer<typeof Attribution>;
|
|
89
|
+
declare const AgeStats: z.ZodObject<{
|
|
90
|
+
commits: z.ZodNumber;
|
|
91
|
+
avgAgeDays: z.ZodNumber;
|
|
92
|
+
medianAgeDays: z.ZodNumber;
|
|
93
|
+
}, "strip", z.ZodTypeAny, {
|
|
94
|
+
commits: number;
|
|
95
|
+
avgAgeDays: number;
|
|
96
|
+
medianAgeDays: number;
|
|
97
|
+
}, {
|
|
98
|
+
commits: number;
|
|
99
|
+
avgAgeDays: number;
|
|
100
|
+
medianAgeDays: number;
|
|
101
|
+
}>;
|
|
102
|
+
type AgeStats = z.infer<typeof AgeStats>;
|
|
103
|
+
declare const FileCategory: z.ZodEnum<["source", "tests", "migrations", "config", "docs", "generated"]>;
|
|
104
|
+
type FileCategory = z.infer<typeof FileCategory>;
|
|
105
|
+
declare const CategoryCounts: z.ZodObject<{
|
|
106
|
+
source: z.ZodNumber;
|
|
107
|
+
tests: z.ZodNumber;
|
|
108
|
+
migrations: z.ZodNumber;
|
|
109
|
+
config: z.ZodNumber;
|
|
110
|
+
docs: z.ZodNumber;
|
|
111
|
+
generated: z.ZodNumber;
|
|
112
|
+
}, "strip", z.ZodTypeAny, {
|
|
113
|
+
source: number;
|
|
114
|
+
tests: number;
|
|
115
|
+
migrations: number;
|
|
116
|
+
config: number;
|
|
117
|
+
docs: number;
|
|
118
|
+
generated: number;
|
|
119
|
+
}, {
|
|
120
|
+
source: number;
|
|
121
|
+
tests: number;
|
|
122
|
+
migrations: number;
|
|
123
|
+
config: number;
|
|
124
|
+
docs: number;
|
|
125
|
+
generated: number;
|
|
126
|
+
}>;
|
|
127
|
+
type CategoryCounts = z.infer<typeof CategoryCounts>;
|
|
128
|
+
declare const CohortContext: z.ZodObject<{
|
|
129
|
+
age: z.ZodNullable<z.ZodObject<{
|
|
130
|
+
commits: z.ZodNumber;
|
|
131
|
+
avgAgeDays: z.ZodNumber;
|
|
132
|
+
medianAgeDays: z.ZodNumber;
|
|
133
|
+
}, "strip", z.ZodTypeAny, {
|
|
134
|
+
commits: number;
|
|
135
|
+
avgAgeDays: number;
|
|
136
|
+
medianAgeDays: number;
|
|
137
|
+
}, {
|
|
138
|
+
commits: number;
|
|
139
|
+
avgAgeDays: number;
|
|
140
|
+
medianAgeDays: number;
|
|
141
|
+
}>>;
|
|
142
|
+
taskMix: z.ZodNullable<z.ZodObject<{
|
|
143
|
+
source: z.ZodNumber;
|
|
144
|
+
tests: z.ZodNumber;
|
|
145
|
+
migrations: z.ZodNumber;
|
|
146
|
+
config: z.ZodNumber;
|
|
147
|
+
docs: z.ZodNumber;
|
|
148
|
+
generated: z.ZodNumber;
|
|
149
|
+
}, "strip", z.ZodTypeAny, {
|
|
150
|
+
source: number;
|
|
151
|
+
tests: number;
|
|
152
|
+
migrations: number;
|
|
153
|
+
config: number;
|
|
154
|
+
docs: number;
|
|
155
|
+
generated: number;
|
|
156
|
+
}, {
|
|
157
|
+
source: number;
|
|
158
|
+
tests: number;
|
|
159
|
+
migrations: number;
|
|
160
|
+
config: number;
|
|
161
|
+
docs: number;
|
|
162
|
+
generated: number;
|
|
163
|
+
}>>;
|
|
164
|
+
}, "strip", z.ZodTypeAny, {
|
|
165
|
+
age: {
|
|
166
|
+
commits: number;
|
|
167
|
+
avgAgeDays: number;
|
|
168
|
+
medianAgeDays: number;
|
|
169
|
+
} | null;
|
|
170
|
+
taskMix: {
|
|
171
|
+
source: number;
|
|
172
|
+
tests: number;
|
|
173
|
+
migrations: number;
|
|
174
|
+
config: number;
|
|
175
|
+
docs: number;
|
|
176
|
+
generated: number;
|
|
177
|
+
} | null;
|
|
178
|
+
}, {
|
|
179
|
+
age: {
|
|
180
|
+
commits: number;
|
|
181
|
+
avgAgeDays: number;
|
|
182
|
+
medianAgeDays: number;
|
|
183
|
+
} | null;
|
|
184
|
+
taskMix: {
|
|
185
|
+
source: number;
|
|
186
|
+
tests: number;
|
|
187
|
+
migrations: number;
|
|
188
|
+
config: number;
|
|
189
|
+
docs: number;
|
|
190
|
+
generated: number;
|
|
191
|
+
} | null;
|
|
192
|
+
}>;
|
|
193
|
+
type CohortContext = z.infer<typeof CohortContext>;
|
|
33
194
|
declare const MergeRatio: z.ZodObject<{
|
|
34
195
|
aiCommitsTotal: z.ZodNumber;
|
|
35
196
|
aiCommitsMerged: z.ZodNumber;
|
|
@@ -243,6 +404,38 @@ declare const Metrics: z.ZodObject<{
|
|
|
243
404
|
defaultAttribution: z.ZodEnum<["ai", "human", "unknown"]>;
|
|
244
405
|
coverageThreshold: z.ZodNumber;
|
|
245
406
|
belowThreshold: z.ZodBoolean;
|
|
407
|
+
modes: z.ZodObject<{
|
|
408
|
+
none: z.ZodNumber;
|
|
409
|
+
autocomplete: z.ZodNumber;
|
|
410
|
+
assisted: z.ZodNumber;
|
|
411
|
+
agent: z.ZodNumber;
|
|
412
|
+
unknown: z.ZodNumber;
|
|
413
|
+
}, "strip", z.ZodTypeAny, {
|
|
414
|
+
unknown: number;
|
|
415
|
+
none: number;
|
|
416
|
+
autocomplete: number;
|
|
417
|
+
assisted: number;
|
|
418
|
+
agent: number;
|
|
419
|
+
}, {
|
|
420
|
+
unknown: number;
|
|
421
|
+
none: number;
|
|
422
|
+
autocomplete: number;
|
|
423
|
+
assisted: number;
|
|
424
|
+
agent: number;
|
|
425
|
+
}>;
|
|
426
|
+
modeEvidence: z.ZodObject<{
|
|
427
|
+
declared: z.ZodNumber;
|
|
428
|
+
inferred: z.ZodNumber;
|
|
429
|
+
none: z.ZodNumber;
|
|
430
|
+
}, "strip", z.ZodTypeAny, {
|
|
431
|
+
none: number;
|
|
432
|
+
declared: number;
|
|
433
|
+
inferred: number;
|
|
434
|
+
}, {
|
|
435
|
+
none: number;
|
|
436
|
+
declared: number;
|
|
437
|
+
inferred: number;
|
|
438
|
+
}>;
|
|
246
439
|
}, "strip", z.ZodTypeAny, {
|
|
247
440
|
ai: number;
|
|
248
441
|
human: number;
|
|
@@ -252,6 +445,18 @@ declare const Metrics: z.ZodObject<{
|
|
|
252
445
|
defaultAttribution: "ai" | "human" | "unknown";
|
|
253
446
|
coverageThreshold: number;
|
|
254
447
|
belowThreshold: boolean;
|
|
448
|
+
modes: {
|
|
449
|
+
unknown: number;
|
|
450
|
+
none: number;
|
|
451
|
+
autocomplete: number;
|
|
452
|
+
assisted: number;
|
|
453
|
+
agent: number;
|
|
454
|
+
};
|
|
455
|
+
modeEvidence: {
|
|
456
|
+
none: number;
|
|
457
|
+
declared: number;
|
|
458
|
+
inferred: number;
|
|
459
|
+
};
|
|
255
460
|
}, {
|
|
256
461
|
ai: number;
|
|
257
462
|
human: number;
|
|
@@ -261,6 +466,18 @@ declare const Metrics: z.ZodObject<{
|
|
|
261
466
|
defaultAttribution: "ai" | "human" | "unknown";
|
|
262
467
|
coverageThreshold: number;
|
|
263
468
|
belowThreshold: boolean;
|
|
469
|
+
modes: {
|
|
470
|
+
unknown: number;
|
|
471
|
+
none: number;
|
|
472
|
+
autocomplete: number;
|
|
473
|
+
assisted: number;
|
|
474
|
+
agent: number;
|
|
475
|
+
};
|
|
476
|
+
modeEvidence: {
|
|
477
|
+
none: number;
|
|
478
|
+
declared: number;
|
|
479
|
+
inferred: number;
|
|
480
|
+
};
|
|
264
481
|
}>;
|
|
265
482
|
mergeRatio: z.ZodObject<{
|
|
266
483
|
aiCommitsTotal: z.ZodNumber;
|
|
@@ -321,6 +538,200 @@ declare const Metrics: z.ZodObject<{
|
|
|
321
538
|
d90_plus: number;
|
|
322
539
|
};
|
|
323
540
|
}>;
|
|
541
|
+
cohorts: z.ZodObject<{
|
|
542
|
+
ai: z.ZodObject<{
|
|
543
|
+
age: z.ZodNullable<z.ZodObject<{
|
|
544
|
+
commits: z.ZodNumber;
|
|
545
|
+
avgAgeDays: z.ZodNumber;
|
|
546
|
+
medianAgeDays: z.ZodNumber;
|
|
547
|
+
}, "strip", z.ZodTypeAny, {
|
|
548
|
+
commits: number;
|
|
549
|
+
avgAgeDays: number;
|
|
550
|
+
medianAgeDays: number;
|
|
551
|
+
}, {
|
|
552
|
+
commits: number;
|
|
553
|
+
avgAgeDays: number;
|
|
554
|
+
medianAgeDays: number;
|
|
555
|
+
}>>;
|
|
556
|
+
taskMix: z.ZodNullable<z.ZodObject<{
|
|
557
|
+
source: z.ZodNumber;
|
|
558
|
+
tests: z.ZodNumber;
|
|
559
|
+
migrations: z.ZodNumber;
|
|
560
|
+
config: z.ZodNumber;
|
|
561
|
+
docs: z.ZodNumber;
|
|
562
|
+
generated: z.ZodNumber;
|
|
563
|
+
}, "strip", z.ZodTypeAny, {
|
|
564
|
+
source: number;
|
|
565
|
+
tests: number;
|
|
566
|
+
migrations: number;
|
|
567
|
+
config: number;
|
|
568
|
+
docs: number;
|
|
569
|
+
generated: number;
|
|
570
|
+
}, {
|
|
571
|
+
source: number;
|
|
572
|
+
tests: number;
|
|
573
|
+
migrations: number;
|
|
574
|
+
config: number;
|
|
575
|
+
docs: number;
|
|
576
|
+
generated: number;
|
|
577
|
+
}>>;
|
|
578
|
+
}, "strip", z.ZodTypeAny, {
|
|
579
|
+
age: {
|
|
580
|
+
commits: number;
|
|
581
|
+
avgAgeDays: number;
|
|
582
|
+
medianAgeDays: number;
|
|
583
|
+
} | null;
|
|
584
|
+
taskMix: {
|
|
585
|
+
source: number;
|
|
586
|
+
tests: number;
|
|
587
|
+
migrations: number;
|
|
588
|
+
config: number;
|
|
589
|
+
docs: number;
|
|
590
|
+
generated: number;
|
|
591
|
+
} | null;
|
|
592
|
+
}, {
|
|
593
|
+
age: {
|
|
594
|
+
commits: number;
|
|
595
|
+
avgAgeDays: number;
|
|
596
|
+
medianAgeDays: number;
|
|
597
|
+
} | null;
|
|
598
|
+
taskMix: {
|
|
599
|
+
source: number;
|
|
600
|
+
tests: number;
|
|
601
|
+
migrations: number;
|
|
602
|
+
config: number;
|
|
603
|
+
docs: number;
|
|
604
|
+
generated: number;
|
|
605
|
+
} | null;
|
|
606
|
+
}>;
|
|
607
|
+
baseline: z.ZodObject<{
|
|
608
|
+
age: z.ZodNullable<z.ZodObject<{
|
|
609
|
+
commits: z.ZodNumber;
|
|
610
|
+
avgAgeDays: z.ZodNumber;
|
|
611
|
+
medianAgeDays: z.ZodNumber;
|
|
612
|
+
}, "strip", z.ZodTypeAny, {
|
|
613
|
+
commits: number;
|
|
614
|
+
avgAgeDays: number;
|
|
615
|
+
medianAgeDays: number;
|
|
616
|
+
}, {
|
|
617
|
+
commits: number;
|
|
618
|
+
avgAgeDays: number;
|
|
619
|
+
medianAgeDays: number;
|
|
620
|
+
}>>;
|
|
621
|
+
taskMix: z.ZodNullable<z.ZodObject<{
|
|
622
|
+
source: z.ZodNumber;
|
|
623
|
+
tests: z.ZodNumber;
|
|
624
|
+
migrations: z.ZodNumber;
|
|
625
|
+
config: z.ZodNumber;
|
|
626
|
+
docs: z.ZodNumber;
|
|
627
|
+
generated: z.ZodNumber;
|
|
628
|
+
}, "strip", z.ZodTypeAny, {
|
|
629
|
+
source: number;
|
|
630
|
+
tests: number;
|
|
631
|
+
migrations: number;
|
|
632
|
+
config: number;
|
|
633
|
+
docs: number;
|
|
634
|
+
generated: number;
|
|
635
|
+
}, {
|
|
636
|
+
source: number;
|
|
637
|
+
tests: number;
|
|
638
|
+
migrations: number;
|
|
639
|
+
config: number;
|
|
640
|
+
docs: number;
|
|
641
|
+
generated: number;
|
|
642
|
+
}>>;
|
|
643
|
+
}, "strip", z.ZodTypeAny, {
|
|
644
|
+
age: {
|
|
645
|
+
commits: number;
|
|
646
|
+
avgAgeDays: number;
|
|
647
|
+
medianAgeDays: number;
|
|
648
|
+
} | null;
|
|
649
|
+
taskMix: {
|
|
650
|
+
source: number;
|
|
651
|
+
tests: number;
|
|
652
|
+
migrations: number;
|
|
653
|
+
config: number;
|
|
654
|
+
docs: number;
|
|
655
|
+
generated: number;
|
|
656
|
+
} | null;
|
|
657
|
+
}, {
|
|
658
|
+
age: {
|
|
659
|
+
commits: number;
|
|
660
|
+
avgAgeDays: number;
|
|
661
|
+
medianAgeDays: number;
|
|
662
|
+
} | null;
|
|
663
|
+
taskMix: {
|
|
664
|
+
source: number;
|
|
665
|
+
tests: number;
|
|
666
|
+
migrations: number;
|
|
667
|
+
config: number;
|
|
668
|
+
docs: number;
|
|
669
|
+
generated: number;
|
|
670
|
+
} | null;
|
|
671
|
+
}>;
|
|
672
|
+
}, "strip", z.ZodTypeAny, {
|
|
673
|
+
ai: {
|
|
674
|
+
age: {
|
|
675
|
+
commits: number;
|
|
676
|
+
avgAgeDays: number;
|
|
677
|
+
medianAgeDays: number;
|
|
678
|
+
} | null;
|
|
679
|
+
taskMix: {
|
|
680
|
+
source: number;
|
|
681
|
+
tests: number;
|
|
682
|
+
migrations: number;
|
|
683
|
+
config: number;
|
|
684
|
+
docs: number;
|
|
685
|
+
generated: number;
|
|
686
|
+
} | null;
|
|
687
|
+
};
|
|
688
|
+
baseline: {
|
|
689
|
+
age: {
|
|
690
|
+
commits: number;
|
|
691
|
+
avgAgeDays: number;
|
|
692
|
+
medianAgeDays: number;
|
|
693
|
+
} | null;
|
|
694
|
+
taskMix: {
|
|
695
|
+
source: number;
|
|
696
|
+
tests: number;
|
|
697
|
+
migrations: number;
|
|
698
|
+
config: number;
|
|
699
|
+
docs: number;
|
|
700
|
+
generated: number;
|
|
701
|
+
} | null;
|
|
702
|
+
};
|
|
703
|
+
}, {
|
|
704
|
+
ai: {
|
|
705
|
+
age: {
|
|
706
|
+
commits: number;
|
|
707
|
+
avgAgeDays: number;
|
|
708
|
+
medianAgeDays: number;
|
|
709
|
+
} | null;
|
|
710
|
+
taskMix: {
|
|
711
|
+
source: number;
|
|
712
|
+
tests: number;
|
|
713
|
+
migrations: number;
|
|
714
|
+
config: number;
|
|
715
|
+
docs: number;
|
|
716
|
+
generated: number;
|
|
717
|
+
} | null;
|
|
718
|
+
};
|
|
719
|
+
baseline: {
|
|
720
|
+
age: {
|
|
721
|
+
commits: number;
|
|
722
|
+
avgAgeDays: number;
|
|
723
|
+
medianAgeDays: number;
|
|
724
|
+
} | null;
|
|
725
|
+
taskMix: {
|
|
726
|
+
source: number;
|
|
727
|
+
tests: number;
|
|
728
|
+
migrations: number;
|
|
729
|
+
config: number;
|
|
730
|
+
docs: number;
|
|
731
|
+
generated: number;
|
|
732
|
+
} | null;
|
|
733
|
+
};
|
|
734
|
+
}>;
|
|
324
735
|
baseline: z.ZodNullable<z.ZodObject<{
|
|
325
736
|
assumed: z.ZodBoolean;
|
|
326
737
|
mergeRatio: z.ZodObject<{
|
|
@@ -452,6 +863,18 @@ declare const Metrics: z.ZodObject<{
|
|
|
452
863
|
defaultAttribution: "ai" | "human" | "unknown";
|
|
453
864
|
coverageThreshold: number;
|
|
454
865
|
belowThreshold: boolean;
|
|
866
|
+
modes: {
|
|
867
|
+
unknown: number;
|
|
868
|
+
none: number;
|
|
869
|
+
autocomplete: number;
|
|
870
|
+
assisted: number;
|
|
871
|
+
agent: number;
|
|
872
|
+
};
|
|
873
|
+
modeEvidence: {
|
|
874
|
+
none: number;
|
|
875
|
+
declared: number;
|
|
876
|
+
inferred: number;
|
|
877
|
+
};
|
|
455
878
|
};
|
|
456
879
|
mergeRatio: {
|
|
457
880
|
aiCommitsTotal: number;
|
|
@@ -470,6 +893,38 @@ declare const Metrics: z.ZodObject<{
|
|
|
470
893
|
d90_plus: number;
|
|
471
894
|
};
|
|
472
895
|
};
|
|
896
|
+
cohorts: {
|
|
897
|
+
ai: {
|
|
898
|
+
age: {
|
|
899
|
+
commits: number;
|
|
900
|
+
avgAgeDays: number;
|
|
901
|
+
medianAgeDays: number;
|
|
902
|
+
} | null;
|
|
903
|
+
taskMix: {
|
|
904
|
+
source: number;
|
|
905
|
+
tests: number;
|
|
906
|
+
migrations: number;
|
|
907
|
+
config: number;
|
|
908
|
+
docs: number;
|
|
909
|
+
generated: number;
|
|
910
|
+
} | null;
|
|
911
|
+
};
|
|
912
|
+
baseline: {
|
|
913
|
+
age: {
|
|
914
|
+
commits: number;
|
|
915
|
+
avgAgeDays: number;
|
|
916
|
+
medianAgeDays: number;
|
|
917
|
+
} | null;
|
|
918
|
+
taskMix: {
|
|
919
|
+
source: number;
|
|
920
|
+
tests: number;
|
|
921
|
+
migrations: number;
|
|
922
|
+
config: number;
|
|
923
|
+
docs: number;
|
|
924
|
+
generated: number;
|
|
925
|
+
} | null;
|
|
926
|
+
};
|
|
927
|
+
};
|
|
473
928
|
baseline: {
|
|
474
929
|
mergeRatio: {
|
|
475
930
|
commitsTotal: number;
|
|
@@ -513,6 +968,18 @@ declare const Metrics: z.ZodObject<{
|
|
|
513
968
|
defaultAttribution: "ai" | "human" | "unknown";
|
|
514
969
|
coverageThreshold: number;
|
|
515
970
|
belowThreshold: boolean;
|
|
971
|
+
modes: {
|
|
972
|
+
unknown: number;
|
|
973
|
+
none: number;
|
|
974
|
+
autocomplete: number;
|
|
975
|
+
assisted: number;
|
|
976
|
+
agent: number;
|
|
977
|
+
};
|
|
978
|
+
modeEvidence: {
|
|
979
|
+
none: number;
|
|
980
|
+
declared: number;
|
|
981
|
+
inferred: number;
|
|
982
|
+
};
|
|
516
983
|
};
|
|
517
984
|
mergeRatio: {
|
|
518
985
|
aiCommitsTotal: number;
|
|
@@ -531,6 +998,38 @@ declare const Metrics: z.ZodObject<{
|
|
|
531
998
|
d90_plus: number;
|
|
532
999
|
};
|
|
533
1000
|
};
|
|
1001
|
+
cohorts: {
|
|
1002
|
+
ai: {
|
|
1003
|
+
age: {
|
|
1004
|
+
commits: number;
|
|
1005
|
+
avgAgeDays: number;
|
|
1006
|
+
medianAgeDays: number;
|
|
1007
|
+
} | null;
|
|
1008
|
+
taskMix: {
|
|
1009
|
+
source: number;
|
|
1010
|
+
tests: number;
|
|
1011
|
+
migrations: number;
|
|
1012
|
+
config: number;
|
|
1013
|
+
docs: number;
|
|
1014
|
+
generated: number;
|
|
1015
|
+
} | null;
|
|
1016
|
+
};
|
|
1017
|
+
baseline: {
|
|
1018
|
+
age: {
|
|
1019
|
+
commits: number;
|
|
1020
|
+
avgAgeDays: number;
|
|
1021
|
+
medianAgeDays: number;
|
|
1022
|
+
} | null;
|
|
1023
|
+
taskMix: {
|
|
1024
|
+
source: number;
|
|
1025
|
+
tests: number;
|
|
1026
|
+
migrations: number;
|
|
1027
|
+
config: number;
|
|
1028
|
+
docs: number;
|
|
1029
|
+
generated: number;
|
|
1030
|
+
} | null;
|
|
1031
|
+
};
|
|
1032
|
+
};
|
|
534
1033
|
baseline: {
|
|
535
1034
|
mergeRatio: {
|
|
536
1035
|
commitsTotal: number;
|
|
@@ -560,6 +1059,10 @@ declare const Metrics: z.ZodObject<{
|
|
|
560
1059
|
}>;
|
|
561
1060
|
type Metrics = z.infer<typeof Metrics>;
|
|
562
1061
|
|
|
1062
|
+
declare function calculateAgeStats(commits: Commit[], now: Date): AgeStats | null;
|
|
1063
|
+
declare function categorizeFile(path: string): FileCategory;
|
|
1064
|
+
declare function calculateCategoryCounts(commits: Commit[]): CategoryCounts | null;
|
|
1065
|
+
|
|
563
1066
|
declare function calculateMergeRatio(commitStream: CommitStream, isTarget?: (commit: Commit) => boolean): MergeRatio;
|
|
564
1067
|
declare function calculateBaselineMergeRatio(commitStream: CommitStream, isTarget?: (commit: Commit) => boolean): CohortMergeRatio;
|
|
565
1068
|
|
|
@@ -572,4 +1075,4 @@ interface MetricsOptions {
|
|
|
572
1075
|
}
|
|
573
1076
|
declare function calculateMetrics(commitStream: CommitStream, options?: MetricsOptions): Metrics;
|
|
574
1077
|
|
|
575
|
-
export { Attribution, Baseline, CohortMergeRatio, Delta, MergeRatio, Metrics, type MetricsOptions, Persistence, calculateBaselineMergeRatio, calculateBaselinePersistence, calculateMergeRatio, calculateMetrics, calculatePersistence };
|
|
1078
|
+
export { AgeStats, Attribution, Baseline, CategoryCounts, CohortContext, CohortMergeRatio, Delta, FileCategory, MergeRatio, Metrics, type MetricsOptions, Persistence, calculateAgeStats, calculateBaselineMergeRatio, calculateBaselinePersistence, calculateCategoryCounts, calculateMergeRatio, calculateMetrics, calculatePersistence, categorizeFile };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { formatISODate } from "@aida-dev/core";
|
|
3
3
|
|
|
4
|
+
// src/cohort.ts
|
|
5
|
+
import { daysBetween } from "@aida-dev/core";
|
|
6
|
+
function calculateAgeStats(commits, now) {
|
|
7
|
+
if (commits.length === 0) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const ages = commits.map((commit) => daysBetween(new Date(commit.authorDate), now)).sort((a, b) => a - b);
|
|
11
|
+
const avg = ages.reduce((sum, days) => sum + days, 0) / ages.length;
|
|
12
|
+
const median = ages.length % 2 === 0 ? (ages[ages.length / 2 - 1] + ages[ages.length / 2]) / 2 : ages[Math.floor(ages.length / 2)];
|
|
13
|
+
return {
|
|
14
|
+
commits: commits.length,
|
|
15
|
+
avgAgeDays: Math.round(avg * 100) / 100,
|
|
16
|
+
medianAgeDays: Math.round(median * 100) / 100
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function categorizeFile(path) {
|
|
20
|
+
const lower = path.toLowerCase();
|
|
21
|
+
const base = lower.split("/").pop() ?? lower;
|
|
22
|
+
if (/^(pnpm-lock\.yaml|package-lock\.json|yarn\.lock|bun\.lockb?|composer\.lock|cargo\.lock|gemfile\.lock|poetry\.lock|changelog(\.[a-z]+)?)$/.test(
|
|
23
|
+
base
|
|
24
|
+
) || /\.(snap|min\.js|min\.css)$/.test(base) || /(^|\/)(dist|build|out|coverage|\.next|node_modules)\//.test(lower)) {
|
|
25
|
+
return "generated";
|
|
26
|
+
}
|
|
27
|
+
if (/(^|\/)(__tests__|__mocks__|tests?|spec|e2e|cypress)\//.test(lower) || /\.(test|spec)\.[a-z]+$/.test(base)) {
|
|
28
|
+
return "tests";
|
|
29
|
+
}
|
|
30
|
+
if (/(^|\/)migrations?\//.test(lower)) {
|
|
31
|
+
return "migrations";
|
|
32
|
+
}
|
|
33
|
+
if (/\.(md|mdx|rst|adoc|txt)$/.test(base) || /(^|\/)docs?\//.test(lower)) {
|
|
34
|
+
return "docs";
|
|
35
|
+
}
|
|
36
|
+
if (base.startsWith(".") || /\.(json|ya?ml|toml|ini|cfg|conf|env|properties)$/.test(base) || /\.config\.[a-z]+$/.test(base) || /(^|\/)(\.github|\.circleci|\.vscode|config)\//.test(lower) || /^(dockerfile|makefile|tsconfig.*|eslint.*|prettier.*)/.test(base)) {
|
|
37
|
+
return "config";
|
|
38
|
+
}
|
|
39
|
+
return "source";
|
|
40
|
+
}
|
|
41
|
+
function calculateCategoryCounts(commits) {
|
|
42
|
+
if (commits.length === 0) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const counts = {
|
|
46
|
+
source: 0,
|
|
47
|
+
tests: 0,
|
|
48
|
+
migrations: 0,
|
|
49
|
+
config: 0,
|
|
50
|
+
docs: 0,
|
|
51
|
+
generated: 0
|
|
52
|
+
};
|
|
53
|
+
for (const commit of commits) {
|
|
54
|
+
for (const file of commit.stats.files) {
|
|
55
|
+
counts[categorizeFile(file.path)]++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return counts;
|
|
59
|
+
}
|
|
60
|
+
|
|
4
61
|
// src/merge-ratio.ts
|
|
5
62
|
function computeMergeRatio(commits) {
|
|
6
63
|
const merged = commits.filter((commit) => commit.inDefaultBranchAncestry);
|
|
@@ -25,7 +82,7 @@ function calculateBaselineMergeRatio(commitStream, isTarget = (commit) => commit
|
|
|
25
82
|
}
|
|
26
83
|
|
|
27
84
|
// src/persistence.ts
|
|
28
|
-
import { daysBetween } from "@aida-dev/core";
|
|
85
|
+
import { daysBetween as daysBetween2 } from "@aida-dev/core";
|
|
29
86
|
function calculatePersistence(commitStream, isTarget = (commit) => commit.tags.attribution === "ai") {
|
|
30
87
|
const targetCommits = commitStream.commits.filter(isTarget);
|
|
31
88
|
if (targetCommits.length === 0) {
|
|
@@ -74,7 +131,7 @@ function calculatePersistence(commitStream, isTarget = (commit) => commit.tags.a
|
|
|
74
131
|
}
|
|
75
132
|
const persistenceDays = [];
|
|
76
133
|
for (const lifecycle of fileLifecycles.values()) {
|
|
77
|
-
lifecycle.persistenceDays =
|
|
134
|
+
lifecycle.persistenceDays = daysBetween2(lifecycle.firstTargetCommitDate, lifecycle.lastSeenDate);
|
|
78
135
|
persistenceDays.push(lifecycle.persistenceDays);
|
|
79
136
|
}
|
|
80
137
|
const avgDays = persistenceDays.length > 0 ? persistenceDays.reduce((sum, days) => sum + days, 0) / persistenceDays.length : 0;
|
|
@@ -111,7 +168,20 @@ var Attribution = z.object({
|
|
|
111
168
|
defaultAttribution: z.enum(["ai", "human", "unknown"]),
|
|
112
169
|
// prior applied to unknown commits
|
|
113
170
|
coverageThreshold: z.number().min(0).max(1),
|
|
114
|
-
belowThreshold: z.boolean()
|
|
171
|
+
belowThreshold: z.boolean(),
|
|
172
|
+
// Autonomy axis (#25): commit counts per mode and per mode-evidence level
|
|
173
|
+
modes: z.object({
|
|
174
|
+
none: z.number().int().nonnegative(),
|
|
175
|
+
autocomplete: z.number().int().nonnegative(),
|
|
176
|
+
assisted: z.number().int().nonnegative(),
|
|
177
|
+
agent: z.number().int().nonnegative(),
|
|
178
|
+
unknown: z.number().int().nonnegative()
|
|
179
|
+
}),
|
|
180
|
+
modeEvidence: z.object({
|
|
181
|
+
declared: z.number().int().nonnegative(),
|
|
182
|
+
inferred: z.number().int().nonnegative(),
|
|
183
|
+
none: z.number().int().nonnegative()
|
|
184
|
+
})
|
|
115
185
|
});
|
|
116
186
|
var MergeRatio = z.object({
|
|
117
187
|
aiCommitsTotal: z.number().int().nonnegative(),
|
|
@@ -130,6 +200,31 @@ var Persistence = z.object({
|
|
|
130
200
|
d90_plus: z.number().int().nonnegative()
|
|
131
201
|
})
|
|
132
202
|
});
|
|
203
|
+
var AgeStats = z.object({
|
|
204
|
+
commits: z.number().int().nonnegative(),
|
|
205
|
+
avgAgeDays: z.number().nonnegative(),
|
|
206
|
+
medianAgeDays: z.number().nonnegative()
|
|
207
|
+
});
|
|
208
|
+
var FileCategory = z.enum([
|
|
209
|
+
"source",
|
|
210
|
+
"tests",
|
|
211
|
+
"migrations",
|
|
212
|
+
"config",
|
|
213
|
+
"docs",
|
|
214
|
+
"generated"
|
|
215
|
+
]);
|
|
216
|
+
var CategoryCounts = z.object({
|
|
217
|
+
source: z.number().int().nonnegative(),
|
|
218
|
+
tests: z.number().int().nonnegative(),
|
|
219
|
+
migrations: z.number().int().nonnegative(),
|
|
220
|
+
config: z.number().int().nonnegative(),
|
|
221
|
+
docs: z.number().int().nonnegative(),
|
|
222
|
+
generated: z.number().int().nonnegative()
|
|
223
|
+
});
|
|
224
|
+
var CohortContext = z.object({
|
|
225
|
+
age: AgeStats.nullable(),
|
|
226
|
+
taskMix: CategoryCounts.nullable()
|
|
227
|
+
});
|
|
133
228
|
var CohortMergeRatio = z.object({
|
|
134
229
|
commitsTotal: z.number().int().nonnegative(),
|
|
135
230
|
commitsMerged: z.number().int().nonnegative(),
|
|
@@ -158,6 +253,12 @@ var Metrics = z.object({
|
|
|
158
253
|
attribution: Attribution,
|
|
159
254
|
mergeRatio: MergeRatio,
|
|
160
255
|
persistence: Persistence,
|
|
256
|
+
// Fairness context (#29, #36): age and task mix per cohort, so consumers
|
|
257
|
+
// can judge whether the AI-vs-baseline comparison is apples-to-apples.
|
|
258
|
+
cohorts: z.object({
|
|
259
|
+
ai: CohortContext,
|
|
260
|
+
baseline: CohortContext
|
|
261
|
+
}),
|
|
161
262
|
// Null when no commit is attributed 'human' and no defaultAttribution prior
|
|
162
263
|
// assigns the unknowns: AIDA does not invent a comparison cohort.
|
|
163
264
|
baseline: Baseline.nullable(),
|
|
@@ -173,8 +274,12 @@ function round(value, decimals) {
|
|
|
173
274
|
function calculateMetrics(commitStream, options = {}) {
|
|
174
275
|
const { defaultAttribution = "unknown", coverageThreshold = 0.7 } = options;
|
|
175
276
|
const counts = { ai: 0, human: 0, unknown: 0 };
|
|
277
|
+
const modes = { none: 0, autocomplete: 0, assisted: 0, agent: 0, unknown: 0 };
|
|
278
|
+
const modeEvidence = { declared: 0, inferred: 0, none: 0 };
|
|
176
279
|
for (const commit of commitStream.commits) {
|
|
177
280
|
counts[commit.tags.attribution]++;
|
|
281
|
+
modes[commit.tags.mode]++;
|
|
282
|
+
modeEvidence[commit.tags.modeEvidence]++;
|
|
178
283
|
}
|
|
179
284
|
const total = commitStream.commits.length;
|
|
180
285
|
const coverage = total > 0 ? (counts.ai + counts.human) / total : 0;
|
|
@@ -186,13 +291,29 @@ function calculateMetrics(commitStream, options = {}) {
|
|
|
186
291
|
coverage: round(coverage, 4),
|
|
187
292
|
defaultAttribution,
|
|
188
293
|
coverageThreshold,
|
|
189
|
-
belowThreshold: coverage < coverageThreshold
|
|
294
|
+
belowThreshold: coverage < coverageThreshold,
|
|
295
|
+
modes,
|
|
296
|
+
modeEvidence
|
|
190
297
|
};
|
|
191
|
-
const
|
|
192
|
-
const
|
|
298
|
+
const isExcluded = (commit) => commit.tags.sources.includes("manifest:excluded");
|
|
299
|
+
const isAI = (commit) => commit.tags.attribution === "ai" || defaultAttribution === "ai" && commit.tags.attribution === "unknown" && !isExcluded(commit);
|
|
300
|
+
const isBaseline = (commit) => commit.tags.attribution === "human" || defaultAttribution === "human" && commit.tags.attribution === "unknown" && !isExcluded(commit);
|
|
193
301
|
const mergeRatio = calculateMergeRatio(commitStream, isAI);
|
|
194
302
|
const persistence = calculatePersistence(commitStream, isAI);
|
|
195
|
-
const
|
|
303
|
+
const now = /* @__PURE__ */ new Date();
|
|
304
|
+
const aiCommits = commitStream.commits.filter(isAI);
|
|
305
|
+
const baselineCommits = commitStream.commits.filter(isBaseline);
|
|
306
|
+
const cohorts = {
|
|
307
|
+
ai: {
|
|
308
|
+
age: calculateAgeStats(aiCommits, now),
|
|
309
|
+
taskMix: calculateCategoryCounts(aiCommits)
|
|
310
|
+
},
|
|
311
|
+
baseline: {
|
|
312
|
+
age: calculateAgeStats(baselineCommits, now),
|
|
313
|
+
taskMix: calculateCategoryCounts(baselineCommits)
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
const baselineSize = baselineCommits.length;
|
|
196
317
|
const baselineAssumed = defaultAttribution === "human" && counts.unknown > 0;
|
|
197
318
|
const baseline = baselineSize > 0 ? {
|
|
198
319
|
assumed: baselineAssumed,
|
|
@@ -210,6 +331,7 @@ function calculateMetrics(commitStream, options = {}) {
|
|
|
210
331
|
const caveats = [
|
|
211
332
|
`Attribution coverage is ${(coverage * 100).toFixed(1)}%: metrics only describe commits whose provenance is known.`,
|
|
212
333
|
"Persistence is file-level, not line-level.",
|
|
334
|
+
"Persistence comparisons are only meaningful between cohorts of similar age and task mix \u2014 check the cohorts section before reading the delta.",
|
|
213
335
|
"Merge ratio: commits from all branches checked against default branch ancestry. Squash merges may undercount unmerged commits.",
|
|
214
336
|
"Time-windowed collection (--since) also windows the ancestry check: commits merged into the default branch before the window may appear unmerged.",
|
|
215
337
|
"AI tagging uses heuristic patterns; false positives/negatives possible."
|
|
@@ -235,23 +357,31 @@ function calculateMetrics(commitStream, options = {}) {
|
|
|
235
357
|
attribution,
|
|
236
358
|
mergeRatio,
|
|
237
359
|
persistence,
|
|
360
|
+
cohorts,
|
|
238
361
|
baseline,
|
|
239
362
|
delta,
|
|
240
363
|
caveats
|
|
241
364
|
};
|
|
242
365
|
}
|
|
243
366
|
export {
|
|
367
|
+
AgeStats,
|
|
244
368
|
Attribution,
|
|
245
369
|
Baseline,
|
|
370
|
+
CategoryCounts,
|
|
371
|
+
CohortContext,
|
|
246
372
|
CohortMergeRatio,
|
|
247
373
|
Delta,
|
|
374
|
+
FileCategory,
|
|
248
375
|
MergeRatio,
|
|
249
376
|
Metrics,
|
|
250
377
|
Persistence,
|
|
378
|
+
calculateAgeStats,
|
|
251
379
|
calculateBaselineMergeRatio,
|
|
252
380
|
calculateBaselinePersistence,
|
|
381
|
+
calculateCategoryCounts,
|
|
253
382
|
calculateMergeRatio,
|
|
254
383
|
calculateMetrics,
|
|
255
|
-
calculatePersistence
|
|
384
|
+
calculatePersistence,
|
|
385
|
+
categorizeFile
|
|
256
386
|
};
|
|
257
387
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/merge-ratio.ts","../src/persistence.ts","../src/schema/metrics.ts"],"sourcesContent":["import { Commit, CommitStream, formatISODate } from '@aida-dev/core';\nimport { calculateBaselineMergeRatio, calculateMergeRatio } from './merge-ratio.js';\nimport { calculateBaselinePersistence, calculatePersistence } from './persistence.js';\nimport { Attribution, Metrics } from './schema/metrics.js';\n\nexport * from './schema/metrics.js';\nexport * from './merge-ratio.js';\nexport * from './persistence.js';\n\nexport interface MetricsOptions {\n // Prior applied to 'unknown' commits: which cohort (if any) they join.\n defaultAttribution?: 'ai' | 'human' | 'unknown';\n coverageThreshold?: number;\n}\n\nfunction round(value: number, decimals: number): number {\n const factor = 10 ** decimals;\n return Math.round(value * factor) / factor;\n}\n\nexport function calculateMetrics(\n commitStream: CommitStream,\n options: MetricsOptions = {}\n): Metrics {\n const { defaultAttribution = 'unknown', coverageThreshold = 0.7 } = options;\n\n const counts = { ai: 0, human: 0, unknown: 0 };\n for (const commit of commitStream.commits) {\n counts[commit.tags.attribution]++;\n }\n const total = commitStream.commits.length;\n const coverage = total > 0 ? (counts.ai + counts.human) / total : 0;\n\n const attribution: Attribution = {\n commitsTotal: total,\n ai: counts.ai,\n human: counts.human,\n unknown: counts.unknown,\n coverage: round(coverage, 4),\n defaultAttribution,\n coverageThreshold,\n belowThreshold: coverage < coverageThreshold,\n };\n\n // Cohort membership: unknown commits join a cohort only via the prior.\n const isAI = (commit: Commit) =>\n commit.tags.attribution === 'ai' ||\n (defaultAttribution === 'ai' && commit.tags.attribution === 'unknown');\n const isBaseline = (commit: Commit) =>\n commit.tags.attribution === 'human' ||\n (defaultAttribution === 'human' && commit.tags.attribution === 'unknown');\n\n const mergeRatio = calculateMergeRatio(commitStream, isAI);\n const persistence = calculatePersistence(commitStream, isAI);\n\n // No baseline cohort → no baseline, no delta. AIDA does not invent a\n // comparison out of unattributed commits.\n const baselineSize = commitStream.commits.filter(isBaseline).length;\n const baselineAssumed = defaultAttribution === 'human' && counts.unknown > 0;\n\n const baseline =\n baselineSize > 0\n ? {\n assumed: baselineAssumed,\n mergeRatio: calculateBaselineMergeRatio(commitStream, isBaseline),\n persistence: calculateBaselinePersistence(commitStream, isBaseline),\n }\n : null;\n\n const delta = baseline\n ? {\n mergeRatio: round(mergeRatio.mergeRatio - baseline.mergeRatio.mergeRatio, 4),\n avgPersistenceDays: round(persistence.avgDays - baseline.persistence.avgDays, 2),\n medianPersistenceDays: round(\n persistence.medianDays - baseline.persistence.medianDays,\n 2\n ),\n }\n : null;\n\n const caveats = [\n `Attribution coverage is ${(coverage * 100).toFixed(1)}%: metrics only describe commits whose provenance is known.`,\n 'Persistence is file-level, not line-level.',\n 'Merge ratio: commits from all branches checked against default branch ancestry. Squash merges may undercount unmerged commits.',\n 'Time-windowed collection (--since) also windows the ancestry check: commits merged into the default branch before the window may appear unmerged.',\n 'AI tagging uses heuristic patterns; false positives/negatives possible.',\n ];\n if (baseline?.assumed) {\n caveats.push(\n `Baseline includes ${counts.unknown} unattributed commit(s) assumed human via defaultAttribution — undetected AI usage may leak into it.`\n );\n }\n if (!baseline) {\n caveats.push(\n 'No baseline: no commits are attributed as human. Set defaultAttribution to \"human\" in .aida.json if unattributed commits in this repo are human-authored.'\n );\n }\n\n return {\n generatedAt: formatISODate(new Date()),\n window: {\n since: commitStream.since,\n until: commitStream.until,\n },\n repoPath: commitStream.repoPath,\n defaultBranch: commitStream.defaultBranch,\n attribution,\n mergeRatio,\n persistence,\n baseline,\n delta,\n caveats,\n };\n}\n","import { Commit, CommitStream } from '@aida-dev/core';\nimport { CohortMergeRatio, MergeRatio } from './schema/metrics.js';\n\nfunction computeMergeRatio(commits: Commit[]): CohortMergeRatio {\n const merged = commits.filter((commit) => commit.inDefaultBranchAncestry);\n\n return {\n commitsTotal: commits.length,\n commitsMerged: merged.length,\n mergeRatio: commits.length > 0 ? merged.length / commits.length : 0,\n };\n}\n\nexport function calculateMergeRatio(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'ai'\n): MergeRatio {\n const aiCommits = commitStream.commits.filter(isTarget);\n const result = computeMergeRatio(aiCommits);\n\n return {\n aiCommitsTotal: result.commitsTotal,\n aiCommitsMerged: result.commitsMerged,\n mergeRatio: result.mergeRatio,\n };\n}\n\nexport function calculateBaselineMergeRatio(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'human'\n): CohortMergeRatio {\n const baselineCommits = commitStream.commits.filter(isTarget);\n return computeMergeRatio(baselineCommits);\n}\n","import { Commit, CommitStream, daysBetween } from '@aida-dev/core';\nimport { Persistence } from './schema/metrics.js';\n\ninterface FileLifecycle {\n path: string;\n firstTargetCommitDate: Date;\n lastSeenDate: Date;\n persistenceDays: number;\n}\n\nexport function calculatePersistence(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'ai'\n): Persistence {\n const targetCommits = commitStream.commits.filter(isTarget);\n\n if (targetCommits.length === 0) {\n return {\n commitsConsidered: 0,\n avgDays: 0,\n medianDays: 0,\n buckets: {\n d0_1: 0,\n d2_7: 0,\n d8_30: 0,\n d31_90: 0,\n d90_plus: 0,\n },\n };\n }\n\n // Group files by path and track their lifecycle\n const fileLifecycles = new Map<string, FileLifecycle>();\n\n // Sort commits by date (oldest first)\n const sortedCommits = [...commitStream.commits].sort(\n (a, b) => new Date(a.authorDate).getTime() - new Date(b.authorDate).getTime()\n );\n\n // Track first target commit per file\n for (const commit of sortedCommits) {\n if (isTarget(commit)) {\n const commitDate = new Date(commit.authorDate);\n\n for (const file of commit.stats.files) {\n if (!fileLifecycles.has(file.path)) {\n fileLifecycles.set(file.path, {\n path: file.path,\n firstTargetCommitDate: commitDate,\n lastSeenDate: commitDate,\n persistenceDays: 0,\n });\n }\n }\n }\n }\n\n // Track last seen date for each file\n for (const commit of sortedCommits) {\n const commitDate = new Date(commit.authorDate);\n\n for (const file of commit.stats.files) {\n const lifecycle = fileLifecycles.get(file.path);\n if (lifecycle) {\n // Update last seen date if file is not deleted\n if (file.status !== 'deleted') {\n lifecycle.lastSeenDate = commitDate;\n }\n }\n }\n }\n\n // Calculate persistence days for each file\n const persistenceDays: number[] = [];\n\n for (const lifecycle of fileLifecycles.values()) {\n lifecycle.persistenceDays = daysBetween(lifecycle.firstTargetCommitDate, lifecycle.lastSeenDate);\n persistenceDays.push(lifecycle.persistenceDays);\n }\n\n // Calculate statistics\n const avgDays =\n persistenceDays.length > 0\n ? persistenceDays.reduce((sum, days) => sum + days, 0) / persistenceDays.length\n : 0;\n\n const sortedDays = [...persistenceDays].sort((a, b) => a - b);\n const medianDays =\n sortedDays.length > 0\n ? sortedDays.length % 2 === 0\n ? (sortedDays[sortedDays.length / 2 - 1] + sortedDays[sortedDays.length / 2]) / 2\n : sortedDays[Math.floor(sortedDays.length / 2)]\n : 0;\n\n // Calculate buckets\n const buckets = {\n d0_1: persistenceDays.filter((days) => days <= 1).length,\n d2_7: persistenceDays.filter((days) => days >= 2 && days <= 7).length,\n d8_30: persistenceDays.filter((days) => days >= 8 && days <= 30).length,\n d31_90: persistenceDays.filter((days) => days >= 31 && days <= 90).length,\n d90_plus: persistenceDays.filter((days) => days > 90).length,\n };\n\n return {\n commitsConsidered: targetCommits.length,\n avgDays: Math.round(avgDays * 100) / 100, // Round to 2 decimal places\n medianDays: Math.round(medianDays * 100) / 100,\n buckets,\n };\n}\n\nexport function calculateBaselinePersistence(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'human'\n): Persistence {\n return calculatePersistence(commitStream, isTarget);\n}\n","import { z } from 'zod';\n\n// Attribution coverage (#34): the headline metric. Every other number in this\n// file is only as trustworthy as this block says it is.\nexport const Attribution = z.object({\n commitsTotal: z.number().int().nonnegative(),\n ai: z.number().int().nonnegative(),\n human: z.number().int().nonnegative(),\n unknown: z.number().int().nonnegative(),\n coverage: z.number().min(0).max(1), // (ai + human) / total\n defaultAttribution: z.enum(['ai', 'human', 'unknown']), // prior applied to unknown commits\n coverageThreshold: z.number().min(0).max(1),\n belowThreshold: z.boolean(),\n});\n\nexport const MergeRatio = z.object({\n aiCommitsTotal: z.number().int().nonnegative(),\n aiCommitsMerged: z.number().int().nonnegative(),\n mergeRatio: z.number().min(0).max(1),\n});\n\nexport const Persistence = z.object({\n commitsConsidered: z.number().int().nonnegative(),\n avgDays: z.number().nonnegative(),\n medianDays: z.number().nonnegative(),\n buckets: z.object({\n d0_1: z.number().int().nonnegative(),\n d2_7: z.number().int().nonnegative(),\n d8_30: z.number().int().nonnegative(),\n d31_90: z.number().int().nonnegative(),\n d90_plus: z.number().int().nonnegative(),\n }),\n});\n\nexport const CohortMergeRatio = z.object({\n commitsTotal: z.number().int().nonnegative(),\n commitsMerged: z.number().int().nonnegative(),\n mergeRatio: z.number().min(0).max(1),\n});\n\nexport const Baseline = z.object({\n // True when the cohort includes 'unknown' commits via defaultAttribution:\n // the baseline is an assumption, not observed attribution.\n assumed: z.boolean(),\n mergeRatio: CohortMergeRatio,\n persistence: Persistence,\n});\n\nexport const Delta = z.object({\n mergeRatio: z.number(),\n avgPersistenceDays: z.number(),\n medianPersistenceDays: z.number(),\n});\n\nexport const Metrics = z.object({\n generatedAt: z.string().datetime(),\n window: z.object({\n since: z.string().optional(),\n until: z.string().optional(),\n }),\n repoPath: z.string(),\n defaultBranch: z.string(),\n attribution: Attribution,\n mergeRatio: MergeRatio,\n persistence: Persistence,\n // Null when no commit is attributed 'human' and no defaultAttribution prior\n // assigns the unknowns: AIDA does not invent a comparison cohort.\n baseline: Baseline.nullable(),\n delta: Delta.nullable(),\n caveats: z.array(z.string()),\n});\n\nexport type Attribution = z.infer<typeof Attribution>;\nexport type MergeRatio = z.infer<typeof MergeRatio>;\nexport type Persistence = z.infer<typeof Persistence>;\nexport type CohortMergeRatio = z.infer<typeof CohortMergeRatio>;\nexport type Baseline = z.infer<typeof Baseline>;\nexport type Delta = z.infer<typeof Delta>;\nexport type Metrics = z.infer<typeof Metrics>;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/cohort.ts","../src/merge-ratio.ts","../src/persistence.ts","../src/schema/metrics.ts"],"sourcesContent":["import { Commit, CommitStream, formatISODate } from '@aida-dev/core';\nimport { calculateAgeStats, calculateCategoryCounts } from './cohort.js';\nimport { calculateBaselineMergeRatio, calculateMergeRatio } from './merge-ratio.js';\nimport { calculateBaselinePersistence, calculatePersistence } from './persistence.js';\nimport { Attribution, Metrics } from './schema/metrics.js';\n\nexport * from './schema/metrics.js';\nexport * from './cohort.js';\nexport * from './merge-ratio.js';\nexport * from './persistence.js';\n\nexport interface MetricsOptions {\n // Prior applied to 'unknown' commits: which cohort (if any) they join.\n defaultAttribution?: 'ai' | 'human' | 'unknown';\n coverageThreshold?: number;\n}\n\nfunction round(value: number, decimals: number): number {\n const factor = 10 ** decimals;\n return Math.round(value * factor) / factor;\n}\n\nexport function calculateMetrics(\n commitStream: CommitStream,\n options: MetricsOptions = {}\n): Metrics {\n const { defaultAttribution = 'unknown', coverageThreshold = 0.7 } = options;\n\n const counts = { ai: 0, human: 0, unknown: 0 };\n const modes = { none: 0, autocomplete: 0, assisted: 0, agent: 0, unknown: 0 };\n const modeEvidence = { declared: 0, inferred: 0, none: 0 };\n for (const commit of commitStream.commits) {\n counts[commit.tags.attribution]++;\n modes[commit.tags.mode]++;\n modeEvidence[commit.tags.modeEvidence]++;\n }\n const total = commitStream.commits.length;\n const coverage = total > 0 ? (counts.ai + counts.human) / total : 0;\n\n const attribution: Attribution = {\n commitsTotal: total,\n ai: counts.ai,\n human: counts.human,\n unknown: counts.unknown,\n coverage: round(coverage, 4),\n defaultAttribution,\n coverageThreshold,\n belowThreshold: coverage < coverageThreshold,\n modes,\n modeEvidence,\n };\n\n // Cohort membership: unknown commits join a cohort only via the prior.\n // Manifest-excluded commits (release bots, merges) never do — they were\n // excluded precisely to stay out of both cohorts.\n const isExcluded = (commit: Commit) => commit.tags.sources.includes('manifest:excluded');\n const isAI = (commit: Commit) =>\n commit.tags.attribution === 'ai' ||\n (defaultAttribution === 'ai' && commit.tags.attribution === 'unknown' && !isExcluded(commit));\n const isBaseline = (commit: Commit) =>\n commit.tags.attribution === 'human' ||\n (defaultAttribution === 'human' &&\n commit.tags.attribution === 'unknown' &&\n !isExcluded(commit));\n\n const mergeRatio = calculateMergeRatio(commitStream, isAI);\n const persistence = calculatePersistence(commitStream, isAI);\n\n // Fairness context (#29, #36): cohort age and task mix\n const now = new Date();\n const aiCommits = commitStream.commits.filter(isAI);\n const baselineCommits = commitStream.commits.filter(isBaseline);\n const cohorts = {\n ai: {\n age: calculateAgeStats(aiCommits, now),\n taskMix: calculateCategoryCounts(aiCommits),\n },\n baseline: {\n age: calculateAgeStats(baselineCommits, now),\n taskMix: calculateCategoryCounts(baselineCommits),\n },\n };\n\n // No baseline cohort → no baseline, no delta. AIDA does not invent a\n // comparison out of unattributed commits.\n const baselineSize = baselineCommits.length;\n const baselineAssumed = defaultAttribution === 'human' && counts.unknown > 0;\n\n const baseline =\n baselineSize > 0\n ? {\n assumed: baselineAssumed,\n mergeRatio: calculateBaselineMergeRatio(commitStream, isBaseline),\n persistence: calculateBaselinePersistence(commitStream, isBaseline),\n }\n : null;\n\n const delta = baseline\n ? {\n mergeRatio: round(mergeRatio.mergeRatio - baseline.mergeRatio.mergeRatio, 4),\n avgPersistenceDays: round(persistence.avgDays - baseline.persistence.avgDays, 2),\n medianPersistenceDays: round(\n persistence.medianDays - baseline.persistence.medianDays,\n 2\n ),\n }\n : null;\n\n const caveats = [\n `Attribution coverage is ${(coverage * 100).toFixed(1)}%: metrics only describe commits whose provenance is known.`,\n 'Persistence is file-level, not line-level.',\n 'Persistence comparisons are only meaningful between cohorts of similar age and task mix — check the cohorts section before reading the delta.',\n 'Merge ratio: commits from all branches checked against default branch ancestry. Squash merges may undercount unmerged commits.',\n 'Time-windowed collection (--since) also windows the ancestry check: commits merged into the default branch before the window may appear unmerged.',\n 'AI tagging uses heuristic patterns; false positives/negatives possible.',\n ];\n if (baseline?.assumed) {\n caveats.push(\n `Baseline includes ${counts.unknown} unattributed commit(s) assumed human via defaultAttribution — undetected AI usage may leak into it.`\n );\n }\n if (!baseline) {\n caveats.push(\n 'No baseline: no commits are attributed as human. Set defaultAttribution to \"human\" in .aida.json if unattributed commits in this repo are human-authored.'\n );\n }\n\n return {\n generatedAt: formatISODate(new Date()),\n window: {\n since: commitStream.since,\n until: commitStream.until,\n },\n repoPath: commitStream.repoPath,\n defaultBranch: commitStream.defaultBranch,\n attribution,\n mergeRatio,\n persistence,\n cohorts,\n baseline,\n delta,\n caveats,\n };\n}\n","import { Commit, daysBetween } from '@aida-dev/core';\nimport { AgeStats, CategoryCounts, FileCategory } from './schema/metrics.js';\n\n// Cohort age (#29): persistence comparisons between cohorts of different\n// ages are misleading — old commits have had more time to accumulate\n// survival. Reporting each cohort's age lets consumers judge fairness.\nexport function calculateAgeStats(commits: Commit[], now: Date): AgeStats | null {\n if (commits.length === 0) {\n return null;\n }\n\n const ages = commits\n .map((commit) => daysBetween(new Date(commit.authorDate), now))\n .sort((a, b) => a - b);\n\n const avg = ages.reduce((sum, days) => sum + days, 0) / ages.length;\n const median =\n ages.length % 2 === 0\n ? (ages[ages.length / 2 - 1] + ages[ages.length / 2]) / 2\n : ages[Math.floor(ages.length / 2)];\n\n return {\n commits: commits.length,\n avgAgeDays: Math.round(avg * 100) / 100,\n medianAgeDays: Math.round(median * 100) / 100,\n };\n}\n\n// File categorization (#36, step 1): AI is often pointed at boilerplate,\n// tests, and migrations, which survive longer because nobody touches them.\n// Reporting each cohort's category mix shows when an AI-vs-baseline\n// comparison is apples-to-oranges. Order matters: first match wins.\nexport function categorizeFile(path: string): FileCategory {\n const lower = path.toLowerCase();\n const base = lower.split('/').pop() ?? lower;\n\n // Generated artifacts (lockfiles, changelogs, snapshots, build output)\n if (\n /^(pnpm-lock\\.yaml|package-lock\\.json|yarn\\.lock|bun\\.lockb?|composer\\.lock|cargo\\.lock|gemfile\\.lock|poetry\\.lock|changelog(\\.[a-z]+)?)$/.test(\n base\n ) ||\n /\\.(snap|min\\.js|min\\.css)$/.test(base) ||\n /(^|\\/)(dist|build|out|coverage|\\.next|node_modules)\\//.test(lower)\n ) {\n return 'generated';\n }\n\n if (\n /(^|\\/)(__tests__|__mocks__|tests?|spec|e2e|cypress)\\//.test(lower) ||\n /\\.(test|spec)\\.[a-z]+$/.test(base)\n ) {\n return 'tests';\n }\n\n if (/(^|\\/)migrations?\\//.test(lower)) {\n return 'migrations';\n }\n\n if (/\\.(md|mdx|rst|adoc|txt)$/.test(base) || /(^|\\/)docs?\\//.test(lower)) {\n return 'docs';\n }\n\n if (\n base.startsWith('.') ||\n /\\.(json|ya?ml|toml|ini|cfg|conf|env|properties)$/.test(base) ||\n /\\.config\\.[a-z]+$/.test(base) ||\n /(^|\\/)(\\.github|\\.circleci|\\.vscode|config)\\//.test(lower) ||\n /^(dockerfile|makefile|tsconfig.*|eslint.*|prettier.*)/.test(base)\n ) {\n return 'config';\n }\n\n return 'source';\n}\n\nexport function calculateCategoryCounts(commits: Commit[]): CategoryCounts | null {\n if (commits.length === 0) {\n return null;\n }\n\n const counts: CategoryCounts = {\n source: 0,\n tests: 0,\n migrations: 0,\n config: 0,\n docs: 0,\n generated: 0,\n };\n\n for (const commit of commits) {\n for (const file of commit.stats.files) {\n counts[categorizeFile(file.path)]++;\n }\n }\n\n return counts;\n}\n","import { Commit, CommitStream } from '@aida-dev/core';\nimport { CohortMergeRatio, MergeRatio } from './schema/metrics.js';\n\nfunction computeMergeRatio(commits: Commit[]): CohortMergeRatio {\n const merged = commits.filter((commit) => commit.inDefaultBranchAncestry);\n\n return {\n commitsTotal: commits.length,\n commitsMerged: merged.length,\n mergeRatio: commits.length > 0 ? merged.length / commits.length : 0,\n };\n}\n\nexport function calculateMergeRatio(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'ai'\n): MergeRatio {\n const aiCommits = commitStream.commits.filter(isTarget);\n const result = computeMergeRatio(aiCommits);\n\n return {\n aiCommitsTotal: result.commitsTotal,\n aiCommitsMerged: result.commitsMerged,\n mergeRatio: result.mergeRatio,\n };\n}\n\nexport function calculateBaselineMergeRatio(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'human'\n): CohortMergeRatio {\n const baselineCommits = commitStream.commits.filter(isTarget);\n return computeMergeRatio(baselineCommits);\n}\n","import { Commit, CommitStream, daysBetween } from '@aida-dev/core';\nimport { Persistence } from './schema/metrics.js';\n\ninterface FileLifecycle {\n path: string;\n firstTargetCommitDate: Date;\n lastSeenDate: Date;\n persistenceDays: number;\n}\n\nexport function calculatePersistence(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'ai'\n): Persistence {\n const targetCommits = commitStream.commits.filter(isTarget);\n\n if (targetCommits.length === 0) {\n return {\n commitsConsidered: 0,\n avgDays: 0,\n medianDays: 0,\n buckets: {\n d0_1: 0,\n d2_7: 0,\n d8_30: 0,\n d31_90: 0,\n d90_plus: 0,\n },\n };\n }\n\n // Group files by path and track their lifecycle\n const fileLifecycles = new Map<string, FileLifecycle>();\n\n // Sort commits by date (oldest first)\n const sortedCommits = [...commitStream.commits].sort(\n (a, b) => new Date(a.authorDate).getTime() - new Date(b.authorDate).getTime()\n );\n\n // Track first target commit per file\n for (const commit of sortedCommits) {\n if (isTarget(commit)) {\n const commitDate = new Date(commit.authorDate);\n\n for (const file of commit.stats.files) {\n if (!fileLifecycles.has(file.path)) {\n fileLifecycles.set(file.path, {\n path: file.path,\n firstTargetCommitDate: commitDate,\n lastSeenDate: commitDate,\n persistenceDays: 0,\n });\n }\n }\n }\n }\n\n // Track last seen date for each file\n for (const commit of sortedCommits) {\n const commitDate = new Date(commit.authorDate);\n\n for (const file of commit.stats.files) {\n const lifecycle = fileLifecycles.get(file.path);\n if (lifecycle) {\n // Update last seen date if file is not deleted\n if (file.status !== 'deleted') {\n lifecycle.lastSeenDate = commitDate;\n }\n }\n }\n }\n\n // Calculate persistence days for each file\n const persistenceDays: number[] = [];\n\n for (const lifecycle of fileLifecycles.values()) {\n lifecycle.persistenceDays = daysBetween(lifecycle.firstTargetCommitDate, lifecycle.lastSeenDate);\n persistenceDays.push(lifecycle.persistenceDays);\n }\n\n // Calculate statistics\n const avgDays =\n persistenceDays.length > 0\n ? persistenceDays.reduce((sum, days) => sum + days, 0) / persistenceDays.length\n : 0;\n\n const sortedDays = [...persistenceDays].sort((a, b) => a - b);\n const medianDays =\n sortedDays.length > 0\n ? sortedDays.length % 2 === 0\n ? (sortedDays[sortedDays.length / 2 - 1] + sortedDays[sortedDays.length / 2]) / 2\n : sortedDays[Math.floor(sortedDays.length / 2)]\n : 0;\n\n // Calculate buckets\n const buckets = {\n d0_1: persistenceDays.filter((days) => days <= 1).length,\n d2_7: persistenceDays.filter((days) => days >= 2 && days <= 7).length,\n d8_30: persistenceDays.filter((days) => days >= 8 && days <= 30).length,\n d31_90: persistenceDays.filter((days) => days >= 31 && days <= 90).length,\n d90_plus: persistenceDays.filter((days) => days > 90).length,\n };\n\n return {\n commitsConsidered: targetCommits.length,\n avgDays: Math.round(avgDays * 100) / 100, // Round to 2 decimal places\n medianDays: Math.round(medianDays * 100) / 100,\n buckets,\n };\n}\n\nexport function calculateBaselinePersistence(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'human'\n): Persistence {\n return calculatePersistence(commitStream, isTarget);\n}\n","import { z } from 'zod';\n\n// Attribution coverage (#34): the headline metric. Every other number in this\n// file is only as trustworthy as this block says it is.\nexport const Attribution = z.object({\n commitsTotal: z.number().int().nonnegative(),\n ai: z.number().int().nonnegative(),\n human: z.number().int().nonnegative(),\n unknown: z.number().int().nonnegative(),\n coverage: z.number().min(0).max(1), // (ai + human) / total\n defaultAttribution: z.enum(['ai', 'human', 'unknown']), // prior applied to unknown commits\n coverageThreshold: z.number().min(0).max(1),\n belowThreshold: z.boolean(),\n // Autonomy axis (#25): commit counts per mode and per mode-evidence level\n modes: z.object({\n none: z.number().int().nonnegative(),\n autocomplete: z.number().int().nonnegative(),\n assisted: z.number().int().nonnegative(),\n agent: z.number().int().nonnegative(),\n unknown: z.number().int().nonnegative(),\n }),\n modeEvidence: z.object({\n declared: z.number().int().nonnegative(),\n inferred: z.number().int().nonnegative(),\n none: z.number().int().nonnegative(),\n }),\n});\n\nexport const MergeRatio = z.object({\n aiCommitsTotal: z.number().int().nonnegative(),\n aiCommitsMerged: z.number().int().nonnegative(),\n mergeRatio: z.number().min(0).max(1),\n});\n\nexport const Persistence = z.object({\n commitsConsidered: z.number().int().nonnegative(),\n avgDays: z.number().nonnegative(),\n medianDays: z.number().nonnegative(),\n buckets: z.object({\n d0_1: z.number().int().nonnegative(),\n d2_7: z.number().int().nonnegative(),\n d8_30: z.number().int().nonnegative(),\n d31_90: z.number().int().nonnegative(),\n d90_plus: z.number().int().nonnegative(),\n }),\n});\n\n// Cohort age (#29): context for judging whether a persistence comparison\n// between cohorts is fair — older cohorts accumulate survival by default.\nexport const AgeStats = z.object({\n commits: z.number().int().nonnegative(),\n avgAgeDays: z.number().nonnegative(),\n medianAgeDays: z.number().nonnegative(),\n});\n\n// Task mix (#36): what kind of files each cohort touched. A persistence\n// comparison is only meaningful when the mixes are similar.\nexport const FileCategory = z.enum([\n 'source',\n 'tests',\n 'migrations',\n 'config',\n 'docs',\n 'generated',\n]);\n\nexport const CategoryCounts = z.object({\n source: z.number().int().nonnegative(),\n tests: z.number().int().nonnegative(),\n migrations: z.number().int().nonnegative(),\n config: z.number().int().nonnegative(),\n docs: z.number().int().nonnegative(),\n generated: z.number().int().nonnegative(),\n});\n\nexport const CohortContext = z.object({\n age: AgeStats.nullable(),\n taskMix: CategoryCounts.nullable(),\n});\n\nexport const CohortMergeRatio = z.object({\n commitsTotal: z.number().int().nonnegative(),\n commitsMerged: z.number().int().nonnegative(),\n mergeRatio: z.number().min(0).max(1),\n});\n\nexport const Baseline = z.object({\n // True when the cohort includes 'unknown' commits via defaultAttribution:\n // the baseline is an assumption, not observed attribution.\n assumed: z.boolean(),\n mergeRatio: CohortMergeRatio,\n persistence: Persistence,\n});\n\nexport const Delta = z.object({\n mergeRatio: z.number(),\n avgPersistenceDays: z.number(),\n medianPersistenceDays: z.number(),\n});\n\nexport const Metrics = z.object({\n generatedAt: z.string().datetime(),\n window: z.object({\n since: z.string().optional(),\n until: z.string().optional(),\n }),\n repoPath: z.string(),\n defaultBranch: z.string(),\n attribution: Attribution,\n mergeRatio: MergeRatio,\n persistence: Persistence,\n // Fairness context (#29, #36): age and task mix per cohort, so consumers\n // can judge whether the AI-vs-baseline comparison is apples-to-apples.\n cohorts: z.object({\n ai: CohortContext,\n baseline: CohortContext,\n }),\n // Null when no commit is attributed 'human' and no defaultAttribution prior\n // assigns the unknowns: AIDA does not invent a comparison cohort.\n baseline: Baseline.nullable(),\n delta: Delta.nullable(),\n caveats: z.array(z.string()),\n});\n\nexport type Attribution = z.infer<typeof Attribution>;\nexport type AgeStats = z.infer<typeof AgeStats>;\nexport type FileCategory = z.infer<typeof FileCategory>;\nexport type CategoryCounts = z.infer<typeof CategoryCounts>;\nexport type CohortContext = z.infer<typeof CohortContext>;\nexport type MergeRatio = z.infer<typeof MergeRatio>;\nexport type Persistence = z.infer<typeof Persistence>;\nexport type CohortMergeRatio = z.infer<typeof CohortMergeRatio>;\nexport type Baseline = z.infer<typeof Baseline>;\nexport type Delta = z.infer<typeof Delta>;\nexport type Metrics = z.infer<typeof Metrics>;\n"],"mappings":"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","names":["daysBetween"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aida-dev/metrics",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Metrics engine for AIDA (AI Development Accounting) - Merge ratio, persistence, and AI impact analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"zod": "^3.22.4",
|
|
14
|
-
"@aida-dev/core": "0.
|
|
14
|
+
"@aida-dev/core": "0.10.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"tsup": "^8.0.0",
|