@atmin.ai/review 0.1.0-alpha.2

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 (48) hide show
  1. package/DEPENDENCIES.md +15 -0
  2. package/LICENSE +55 -0
  3. package/NOTICE +6 -0
  4. package/README.md +144 -0
  5. package/dist/assessment.d.ts +26 -0
  6. package/dist/assessment.js +88 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +100 -0
  9. package/dist/contracts.d.ts +625 -0
  10. package/dist/contracts.js +86 -0
  11. package/dist/cost-report.d.ts +40 -0
  12. package/dist/cost-report.js +42 -0
  13. package/dist/github/api.d.ts +66 -0
  14. package/dist/github/api.js +190 -0
  15. package/dist/github/checks.d.ts +11 -0
  16. package/dist/github/checks.js +42 -0
  17. package/dist/github/cli.d.ts +2 -0
  18. package/dist/github/cli.js +94 -0
  19. package/dist/github/config.d.ts +16 -0
  20. package/dist/github/config.js +41 -0
  21. package/dist/github/runner.d.ts +7 -0
  22. package/dist/github/runner.js +67 -0
  23. package/dist/github/store.d.ts +40 -0
  24. package/dist/github/store.js +134 -0
  25. package/dist/github/task.d.ts +1 -0
  26. package/dist/github/task.js +29 -0
  27. package/dist/github/webhook.d.ts +6 -0
  28. package/dist/github/webhook.js +119 -0
  29. package/dist/github/worker.d.ts +19 -0
  30. package/dist/github/worker.js +209 -0
  31. package/dist/investigation.d.ts +198 -0
  32. package/dist/investigation.js +276 -0
  33. package/dist/openai-model.d.ts +2 -0
  34. package/dist/openai-model.js +41 -0
  35. package/dist/openrouter-model.d.ts +2 -0
  36. package/dist/openrouter-model.js +90 -0
  37. package/dist/provider-error.d.ts +10 -0
  38. package/dist/provider-error.js +21 -0
  39. package/dist/render.d.ts +4 -0
  40. package/dist/render.js +56 -0
  41. package/dist/run.d.ts +6 -0
  42. package/dist/run.js +39 -0
  43. package/dist/snapshot.d.ts +46 -0
  44. package/dist/snapshot.js +246 -0
  45. package/package.json +47 -0
  46. package/profiles/baseline-deepseek.json +10 -0
  47. package/profiles/smoke-openai.json +10 -0
  48. package/profiles/smoke-openrouter-free.json +10 -0
@@ -0,0 +1,625 @@
1
+ export declare class ReviewInputError extends Error {
2
+ }
3
+ export declare const PRIORITIES: readonly ["P0", "P1", "P2", "P3", "P4"];
4
+ export type Priority = typeof PRIORITIES[number];
5
+ export interface Policy {
6
+ schemaVersion: 1;
7
+ rubricVersion: '1';
8
+ includeOptional: boolean;
9
+ requiredChecks: string[];
10
+ }
11
+ export interface ChangedFile {
12
+ path: string;
13
+ change: 'added' | 'modified' | 'deleted';
14
+ kind: 'text' | 'binary' | 'symlink' | 'submodule';
15
+ }
16
+ export interface Packet {
17
+ schemaVersion: 1;
18
+ repository: string;
19
+ pr: number;
20
+ baseRef: string;
21
+ headSha: string;
22
+ baseSha: string;
23
+ mergeBaseSha: string;
24
+ createdAt: string;
25
+ policy: Policy;
26
+ policyHash: string;
27
+ diffHash: string;
28
+ changedFiles: ChangedFile[];
29
+ }
30
+ export interface Anchor {
31
+ path: string;
32
+ side: 'head' | 'base';
33
+ line: number;
34
+ }
35
+ export type EvidenceAnchor = Omit<Anchor, 'line'> & {
36
+ line: number | null;
37
+ };
38
+ interface EvidenceBase {
39
+ id: string;
40
+ summary: string;
41
+ anchors: EvidenceAnchor[];
42
+ }
43
+ export type Evidence = EvidenceBase & ({
44
+ kind: 'source-reasoning' | 'reproduction' | 'ci';
45
+ provenance: 'declared';
46
+ } | {
47
+ kind: 'source-read';
48
+ provenance: 'controller-captured';
49
+ capture: {
50
+ revision: string;
51
+ startLine: number;
52
+ endLine: number;
53
+ totalLines: number;
54
+ contentHash: string;
55
+ };
56
+ });
57
+ export interface Finding {
58
+ id: string;
59
+ priority: Priority;
60
+ kind: 'defect' | 'improvement';
61
+ category: 'correctness' | 'security' | 'data-integrity' | 'reliability' | 'validation' | 'simplicity';
62
+ title: string;
63
+ trigger: string;
64
+ consequence: string;
65
+ priorityReason: string;
66
+ counterEvidence: string;
67
+ suggestion: string;
68
+ anchor: Anchor;
69
+ evidenceIds: string[];
70
+ }
71
+ export interface Result {
72
+ schemaVersion: 1;
73
+ headSha: string;
74
+ baseSha: string;
75
+ policyHash: string;
76
+ status: 'not-started' | 'partial' | 'completed';
77
+ reviewer: {
78
+ name: string;
79
+ model: string;
80
+ context: 'independent' | 'author' | 'unknown';
81
+ };
82
+ summary: string;
83
+ coverage: {
84
+ path: string;
85
+ status: 'reviewed' | 'unreviewed';
86
+ evidenceIds: string[];
87
+ }[];
88
+ validation: {
89
+ name: string;
90
+ status: 'pass' | 'fail' | 'not-run' | 'not-applicable';
91
+ reason: string;
92
+ evidenceIds: string[];
93
+ }[];
94
+ evidence: Evidence[];
95
+ findings: Finding[];
96
+ limitations: string[];
97
+ }
98
+ export declare const text: {
99
+ type: string;
100
+ minLength: number;
101
+ maxLength: number;
102
+ pattern: string;
103
+ };
104
+ export declare const policySchema: {
105
+ type: string;
106
+ properties: {
107
+ schemaVersion: {
108
+ const: number;
109
+ };
110
+ rubricVersion: {
111
+ const: string;
112
+ };
113
+ includeOptional: {
114
+ type: string;
115
+ };
116
+ requiredChecks: {
117
+ uniqueItems: boolean;
118
+ type: string;
119
+ items: {
120
+ type: string;
121
+ minLength: number;
122
+ maxLength: number;
123
+ pattern: string;
124
+ };
125
+ minItems: number;
126
+ maxItems: number;
127
+ };
128
+ };
129
+ required: string[];
130
+ additionalProperties: boolean;
131
+ };
132
+ export declare const packetSchema: {
133
+ type: string;
134
+ properties: {
135
+ schemaVersion: {
136
+ const: number;
137
+ };
138
+ repository: {
139
+ type: string;
140
+ pattern: string;
141
+ };
142
+ pr: {
143
+ type: string;
144
+ minimum: number;
145
+ maximum: number;
146
+ };
147
+ baseRef: {
148
+ type: string;
149
+ minLength: number;
150
+ maxLength: number;
151
+ pattern: string;
152
+ };
153
+ headSha: {
154
+ type: string;
155
+ pattern: string;
156
+ };
157
+ baseSha: {
158
+ type: string;
159
+ pattern: string;
160
+ };
161
+ mergeBaseSha: {
162
+ type: string;
163
+ pattern: string;
164
+ };
165
+ createdAt: {
166
+ type: string;
167
+ pattern: string;
168
+ };
169
+ policy: {
170
+ type: string;
171
+ properties: {
172
+ schemaVersion: {
173
+ const: number;
174
+ };
175
+ rubricVersion: {
176
+ const: string;
177
+ };
178
+ includeOptional: {
179
+ type: string;
180
+ };
181
+ requiredChecks: {
182
+ uniqueItems: boolean;
183
+ type: string;
184
+ items: {
185
+ type: string;
186
+ minLength: number;
187
+ maxLength: number;
188
+ pattern: string;
189
+ };
190
+ minItems: number;
191
+ maxItems: number;
192
+ };
193
+ };
194
+ required: string[];
195
+ additionalProperties: boolean;
196
+ };
197
+ policyHash: {
198
+ type: string;
199
+ pattern: string;
200
+ };
201
+ diffHash: {
202
+ type: string;
203
+ pattern: string;
204
+ };
205
+ changedFiles: {
206
+ type: string;
207
+ items: {
208
+ type: string;
209
+ properties: {
210
+ path: {
211
+ type: string;
212
+ minLength: number;
213
+ maxLength: number;
214
+ pattern: string;
215
+ };
216
+ change: {
217
+ type: string;
218
+ enum: readonly string[];
219
+ };
220
+ kind: {
221
+ type: string;
222
+ enum: readonly string[];
223
+ };
224
+ };
225
+ required: string[];
226
+ additionalProperties: boolean;
227
+ };
228
+ minItems: number;
229
+ maxItems: number;
230
+ };
231
+ };
232
+ required: string[];
233
+ additionalProperties: boolean;
234
+ };
235
+ export declare const resultSchema: {
236
+ type: string;
237
+ properties: {
238
+ schemaVersion: {
239
+ const: number;
240
+ };
241
+ headSha: {
242
+ type: string;
243
+ pattern: string;
244
+ };
245
+ baseSha: {
246
+ type: string;
247
+ pattern: string;
248
+ };
249
+ policyHash: {
250
+ type: string;
251
+ pattern: string;
252
+ };
253
+ status: {
254
+ type: string;
255
+ enum: readonly string[];
256
+ };
257
+ reviewer: {
258
+ type: string;
259
+ properties: {
260
+ name: {
261
+ type: string;
262
+ minLength: number;
263
+ maxLength: number;
264
+ pattern: string;
265
+ };
266
+ model: {
267
+ type: string;
268
+ minLength: number;
269
+ maxLength: number;
270
+ pattern: string;
271
+ };
272
+ context: {
273
+ type: string;
274
+ enum: readonly string[];
275
+ };
276
+ };
277
+ required: string[];
278
+ additionalProperties: boolean;
279
+ };
280
+ summary: {
281
+ type: string;
282
+ minLength: number;
283
+ maxLength: number;
284
+ pattern: string;
285
+ };
286
+ coverage: {
287
+ type: string;
288
+ items: {
289
+ type: string;
290
+ properties: {
291
+ path: {
292
+ type: string;
293
+ minLength: number;
294
+ maxLength: number;
295
+ pattern: string;
296
+ };
297
+ status: {
298
+ type: string;
299
+ enum: readonly string[];
300
+ };
301
+ evidenceIds: {
302
+ uniqueItems: boolean;
303
+ type: string;
304
+ items: {
305
+ type: string;
306
+ minLength: number;
307
+ maxLength: number;
308
+ pattern: string;
309
+ };
310
+ minItems: number;
311
+ maxItems: number;
312
+ };
313
+ };
314
+ required: string[];
315
+ additionalProperties: boolean;
316
+ };
317
+ minItems: number;
318
+ maxItems: number;
319
+ };
320
+ validation: {
321
+ type: string;
322
+ items: {
323
+ type: string;
324
+ properties: {
325
+ name: {
326
+ type: string;
327
+ minLength: number;
328
+ maxLength: number;
329
+ pattern: string;
330
+ };
331
+ status: {
332
+ type: string;
333
+ enum: readonly string[];
334
+ };
335
+ reason: {
336
+ type: string;
337
+ minLength: number;
338
+ maxLength: number;
339
+ pattern: string;
340
+ };
341
+ evidenceIds: {
342
+ uniqueItems: boolean;
343
+ type: string;
344
+ items: {
345
+ type: string;
346
+ minLength: number;
347
+ maxLength: number;
348
+ pattern: string;
349
+ };
350
+ minItems: number;
351
+ maxItems: number;
352
+ };
353
+ };
354
+ required: string[];
355
+ additionalProperties: boolean;
356
+ };
357
+ minItems: number;
358
+ maxItems: number;
359
+ };
360
+ evidence: {
361
+ type: string;
362
+ items: {
363
+ oneOf: ({
364
+ type: string;
365
+ properties: {
366
+ id: {
367
+ type: string;
368
+ minLength: number;
369
+ maxLength: number;
370
+ pattern: string;
371
+ };
372
+ kind: {
373
+ type: string;
374
+ enum: readonly string[];
375
+ };
376
+ provenance: {
377
+ const: string;
378
+ };
379
+ summary: {
380
+ type: string;
381
+ minLength: number;
382
+ maxLength: number;
383
+ pattern: string;
384
+ };
385
+ anchors: {
386
+ type: string;
387
+ items: {
388
+ type: string;
389
+ properties: {
390
+ path: {
391
+ type: string;
392
+ minLength: number;
393
+ maxLength: number;
394
+ pattern: string;
395
+ };
396
+ side: {
397
+ type: string;
398
+ enum: readonly string[];
399
+ };
400
+ line: {
401
+ anyOf: ({
402
+ type: string;
403
+ minimum: number;
404
+ } | {
405
+ type: string;
406
+ minimum?: never;
407
+ })[];
408
+ };
409
+ };
410
+ required: string[];
411
+ additionalProperties: boolean;
412
+ };
413
+ minItems: number;
414
+ maxItems: number;
415
+ };
416
+ };
417
+ required: string[];
418
+ additionalProperties: boolean;
419
+ } | {
420
+ type: string;
421
+ properties: {
422
+ id: {
423
+ type: string;
424
+ minLength: number;
425
+ maxLength: number;
426
+ pattern: string;
427
+ };
428
+ kind: {
429
+ const: string;
430
+ };
431
+ provenance: {
432
+ const: string;
433
+ };
434
+ summary: {
435
+ type: string;
436
+ minLength: number;
437
+ maxLength: number;
438
+ pattern: string;
439
+ };
440
+ anchors: {
441
+ maxItems: number;
442
+ type: string;
443
+ items: {
444
+ type: string;
445
+ properties: {
446
+ path: {
447
+ type: string;
448
+ minLength: number;
449
+ maxLength: number;
450
+ pattern: string;
451
+ };
452
+ side: {
453
+ type: string;
454
+ enum: readonly string[];
455
+ };
456
+ line: {
457
+ anyOf: ({
458
+ type: string;
459
+ minimum: number;
460
+ } | {
461
+ type: string;
462
+ minimum?: never;
463
+ })[];
464
+ };
465
+ };
466
+ required: string[];
467
+ additionalProperties: boolean;
468
+ };
469
+ minItems: number;
470
+ };
471
+ capture: {
472
+ type: string;
473
+ properties: {
474
+ revision: {
475
+ type: string;
476
+ pattern: string;
477
+ };
478
+ startLine: {
479
+ type: string;
480
+ minimum: number;
481
+ };
482
+ endLine: {
483
+ type: string;
484
+ minimum: number;
485
+ };
486
+ totalLines: {
487
+ type: string;
488
+ minimum: number;
489
+ };
490
+ contentHash: {
491
+ type: string;
492
+ pattern: string;
493
+ };
494
+ };
495
+ required: string[];
496
+ additionalProperties: boolean;
497
+ };
498
+ };
499
+ required: string[];
500
+ additionalProperties: boolean;
501
+ })[];
502
+ };
503
+ minItems: number;
504
+ maxItems: number;
505
+ };
506
+ findings: {
507
+ type: string;
508
+ items: {
509
+ type: string;
510
+ properties: {
511
+ id: {
512
+ type: string;
513
+ minLength: number;
514
+ maxLength: number;
515
+ pattern: string;
516
+ };
517
+ priority: {
518
+ type: string;
519
+ enum: readonly string[];
520
+ };
521
+ kind: {
522
+ type: string;
523
+ enum: readonly string[];
524
+ };
525
+ category: {
526
+ type: string;
527
+ enum: readonly string[];
528
+ };
529
+ title: {
530
+ type: string;
531
+ minLength: number;
532
+ maxLength: number;
533
+ pattern: string;
534
+ };
535
+ trigger: {
536
+ type: string;
537
+ minLength: number;
538
+ maxLength: number;
539
+ pattern: string;
540
+ };
541
+ consequence: {
542
+ type: string;
543
+ minLength: number;
544
+ maxLength: number;
545
+ pattern: string;
546
+ };
547
+ priorityReason: {
548
+ type: string;
549
+ minLength: number;
550
+ maxLength: number;
551
+ pattern: string;
552
+ };
553
+ counterEvidence: {
554
+ type: string;
555
+ minLength: number;
556
+ maxLength: number;
557
+ pattern: string;
558
+ };
559
+ suggestion: {
560
+ type: string;
561
+ minLength: number;
562
+ maxLength: number;
563
+ pattern: string;
564
+ };
565
+ anchor: {
566
+ type: string;
567
+ properties: {
568
+ path: {
569
+ type: string;
570
+ minLength: number;
571
+ maxLength: number;
572
+ pattern: string;
573
+ };
574
+ side: {
575
+ type: string;
576
+ enum: readonly string[];
577
+ };
578
+ line: {
579
+ type: string;
580
+ minimum: number;
581
+ };
582
+ };
583
+ required: string[];
584
+ additionalProperties: boolean;
585
+ };
586
+ evidenceIds: {
587
+ minItems: number;
588
+ uniqueItems: boolean;
589
+ type: string;
590
+ items: {
591
+ type: string;
592
+ minLength: number;
593
+ maxLength: number;
594
+ pattern: string;
595
+ };
596
+ maxItems: number;
597
+ };
598
+ };
599
+ required: string[];
600
+ additionalProperties: boolean;
601
+ };
602
+ minItems: number;
603
+ maxItems: number;
604
+ };
605
+ limitations: {
606
+ type: string;
607
+ items: {
608
+ type: string;
609
+ minLength: number;
610
+ maxLength: number;
611
+ pattern: string;
612
+ };
613
+ minItems: number;
614
+ maxItems: number;
615
+ };
616
+ };
617
+ required: string[];
618
+ additionalProperties: boolean;
619
+ };
620
+ export declare function parsePacket(value: unknown): Packet;
621
+ export declare function parseResult(value: unknown): Result;
622
+ export declare function parsePolicy(value: unknown): Policy;
623
+ export declare const defaultPolicy: () => Policy;
624
+ export declare function initialResult(packet: Packet): Result;
625
+ export {};
@@ -0,0 +1,86 @@
1
+ import { Ajv } from 'ajv';
2
+ // Controller-authored validation messages only; never wrap provider or OS errors.
3
+ export class ReviewInputError extends Error {
4
+ }
5
+ export const PRIORITIES = ['P0', 'P1', 'P2', 'P3', 'P4'];
6
+ // Small schema constructors keep every object closed without a second schema framework.
7
+ // Explicit whole-string matching also works with provider grammar decoders.
8
+ export const text = { type: 'string', minLength: 1, maxLength: 16000, pattern: '^[\\s\\S]*\\S[\\s\\S]*$' };
9
+ const sha = { type: 'string', pattern: '^[a-f0-9]{40}$' };
10
+ const digest = { type: 'string', pattern: '^[a-f0-9]{64}$' };
11
+ const choice = (values) => ({ type: 'string', enum: values });
12
+ const array = (items, minItems = 0) => ({ type: 'array', items, minItems, maxItems: 10000 });
13
+ const object = (properties) => ({
14
+ type: 'object', properties, required: Object.keys(properties), additionalProperties: false,
15
+ });
16
+ const refs = { ...array(text), uniqueItems: true };
17
+ const anchor = object({ path: text, side: choice(['head', 'base']), line: { type: 'integer', minimum: 1 } });
18
+ const evidenceAnchor = object({ path: text, side: choice(['head', 'base']), line: { anyOf: [{ type: 'integer', minimum: 1 }, { type: 'null' }] } });
19
+ export const policySchema = object({
20
+ schemaVersion: { const: 1 }, rubricVersion: { const: '1' },
21
+ includeOptional: { type: 'boolean' }, requiredChecks: { ...array(text, 1), uniqueItems: true },
22
+ });
23
+ export const packetSchema = object({
24
+ schemaVersion: { const: 1 },
25
+ repository: { type: 'string', pattern: '^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$' },
26
+ pr: { type: 'integer', minimum: 1, maximum: Number.MAX_SAFE_INTEGER },
27
+ baseRef: text, headSha: sha, baseSha: sha, mergeBaseSha: sha,
28
+ createdAt: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$' },
29
+ policy: policySchema, policyHash: digest, diffHash: digest,
30
+ changedFiles: array(object({ path: text, change: choice(['added', 'modified', 'deleted']), kind: choice(['text', 'binary', 'symlink', 'submodule']) })),
31
+ });
32
+ export const resultSchema = object({
33
+ schemaVersion: { const: 1 }, headSha: sha, baseSha: sha, policyHash: digest,
34
+ status: choice(['not-started', 'partial', 'completed']),
35
+ reviewer: object({ name: text, model: text, context: choice(['independent', 'author', 'unknown']) }),
36
+ summary: text,
37
+ coverage: array(object({ path: text, status: choice(['reviewed', 'unreviewed']), evidenceIds: refs })),
38
+ validation: array(object({ name: text, status: choice(['pass', 'fail', 'not-run', 'not-applicable']), reason: text, evidenceIds: refs })),
39
+ evidence: array({ oneOf: [object({
40
+ id: text, kind: choice(['source-reasoning', 'reproduction', 'ci']), provenance: { const: 'declared' },
41
+ summary: text, anchors: array(evidenceAnchor, 1),
42
+ }), object({
43
+ id: text, kind: { const: 'source-read' }, provenance: { const: 'controller-captured' },
44
+ summary: text, anchors: { ...array(evidenceAnchor, 1), maxItems: 1 },
45
+ capture: object({ revision: sha, startLine: { type: 'integer', minimum: 1 },
46
+ endLine: { type: 'integer', minimum: 0 }, totalLines: { type: 'integer', minimum: 0 }, contentHash: digest }),
47
+ })] }),
48
+ findings: array(object({
49
+ id: text, priority: choice(PRIORITIES), kind: choice(['defect', 'improvement']),
50
+ category: choice(['correctness', 'security', 'data-integrity', 'reliability', 'validation', 'simplicity']),
51
+ title: text, trigger: text, consequence: text, priorityReason: text, counterEvidence: text,
52
+ suggestion: text, anchor, evidenceIds: { ...refs, minItems: 1 },
53
+ })),
54
+ limitations: array(text),
55
+ });
56
+ const ajv = new Ajv({ allErrors: true, strict: true });
57
+ const validatePacketSchema = ajv.compile(packetSchema);
58
+ const validateResultSchema = ajv.compile(resultSchema);
59
+ const validatePolicySchema = ajv.compile(policySchema);
60
+ export function parsePacket(value) {
61
+ if (!validatePacketSchema(value))
62
+ throw new Error(`Invalid packet: ${ajv.errorsText(validatePacketSchema.errors)}`);
63
+ return value;
64
+ }
65
+ export function parseResult(value) {
66
+ if (!validateResultSchema(value))
67
+ throw new Error(`Invalid result: ${ajv.errorsText(validateResultSchema.errors)}`);
68
+ return value;
69
+ }
70
+ export function parsePolicy(value) {
71
+ if (!validatePolicySchema(value))
72
+ throw new Error(`Invalid policy: ${ajv.errorsText(validatePolicySchema.errors)}`);
73
+ // Fixed ordering provides stable hashing independent of JSON property order.
74
+ return { schemaVersion: 1, rubricVersion: '1', includeOptional: value.includeOptional, requiredChecks: value.requiredChecks };
75
+ }
76
+ export const defaultPolicy = () => ({ schemaVersion: 1, rubricVersion: '1', includeOptional: false, requiredChecks: ['change-validation'] });
77
+ export function initialResult(packet) {
78
+ return {
79
+ schemaVersion: 1, headSha: packet.headSha, baseSha: packet.baseSha, policyHash: packet.policyHash,
80
+ status: 'not-started', reviewer: { name: 'unknown', model: 'unknown', context: 'unknown' },
81
+ summary: 'The snapshot is captured. Investigation has not run.',
82
+ coverage: packet.changedFiles.map(({ path }) => ({ path, status: 'unreviewed', evidenceIds: [] })),
83
+ validation: packet.policy.requiredChecks.map(name => ({ name, status: 'not-run', reason: 'Required validation has not run.', evidenceIds: [] })),
84
+ evidence: [], findings: [], limitations: ['Investigation has not started; no repository code has been executed.'],
85
+ };
86
+ }