argus-reviewer-e2e 0.1.3 → 0.3.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 (48) hide show
  1. package/README.md +86 -49
  2. package/action/action.yml +155 -61
  3. package/action/approval-review.mjs +393 -0
  4. package/action/bootstrap.mjs +102 -0
  5. package/action/cli.mjs +36 -0
  6. package/action/emit-review.mjs +336 -0
  7. package/action/runtime.mjs +154 -0
  8. package/action/sticky-comment.cjs +870 -0
  9. package/dist/api.d.ts +2 -0
  10. package/dist/api.js +4 -0
  11. package/dist/cache/store.d.ts +2 -0
  12. package/dist/cache/store.js +10 -3
  13. package/dist/cli.d.ts +137 -0
  14. package/dist/cli.js +677 -60
  15. package/dist/config.d.ts +67 -2
  16. package/dist/config.js +138 -7
  17. package/dist/debug.d.ts +1 -0
  18. package/dist/debug.js +9 -3
  19. package/dist/engine/loop.d.ts +12 -0
  20. package/dist/engine/loop.js +55 -6
  21. package/dist/evidence/ci.d.ts +3 -0
  22. package/dist/evidence/ci.js +3 -1
  23. package/dist/pipeline/budget.d.ts +11 -0
  24. package/dist/pipeline/budget.js +48 -0
  25. package/dist/pipeline/contracts.d.ts +12 -0
  26. package/dist/pipeline/contracts.js +16 -0
  27. package/dist/pipeline/verify.d.ts +27 -0
  28. package/dist/pipeline/verify.js +197 -0
  29. package/dist/probe/queue.d.ts +4 -1
  30. package/dist/probe/queue.js +46 -8
  31. package/dist/report/manifest.d.ts +87 -0
  32. package/dist/report/manifest.js +143 -0
  33. package/dist/report/run.d.ts +9 -0
  34. package/dist/report/run.js +6 -0
  35. package/dist/review/adjudicate.d.ts +63 -0
  36. package/dist/review/adjudicate.js +111 -0
  37. package/dist/review/secrets.d.ts +90 -0
  38. package/dist/review/secrets.js +224 -0
  39. package/dist/review/triage.d.ts +76 -0
  40. package/dist/review/triage.js +163 -0
  41. package/dist/trust.d.ts +50 -0
  42. package/dist/trust.js +103 -0
  43. package/dist/vision/cost.d.ts +14 -1
  44. package/dist/vision/cost.js +13 -0
  45. package/dist/vision/decisions.d.ts +95 -0
  46. package/dist/vision/decisions.js +232 -0
  47. package/package.json +9 -8
  48. package/action/sticky-comment.mjs +0 -404
package/dist/api.d.ts CHANGED
@@ -7,6 +7,7 @@ import { Config, defineConfig } from './config.js';
7
7
  import { ErrorRecord } from './journal/schema.js';
8
8
  import { Logger } from './log.js';
9
9
  export { defineConfig };
10
+ export { DecisionClient, DecisionError, JEV_DEFAULT_MODEL, type DecisionAnswer, type DecisionClientOptions, type DecisionErrorKind, type DecisionQuestion, } from './vision/decisions.js';
10
11
  /**
11
12
  * Test-facing API (R13). Test files are plain TypeScript using a `td` object:
12
13
  *
@@ -91,6 +92,7 @@ export declare class TdSession {
91
92
  private constructor();
92
93
  static create(opts: TdSessionOptions): Promise<TdSession>;
93
94
  get visionCalls(): number;
95
+ get cacheStats(): import("./engine/loop.js").CacheStats;
94
96
  /** Non-fatal anomalies observed by the engine — journaled as evidence. */
95
97
  get errorRecords(): ErrorRecord[];
96
98
  get failed(): boolean;
package/dist/api.js CHANGED
@@ -4,6 +4,7 @@ import { loadFlow, saveFlow } from './cache/store.js';
4
4
  import { Ledger } from './vision/ledger.js';
5
5
  import { defineConfig } from './config.js';
6
6
  export { defineConfig };
7
+ export { DecisionClient, DecisionError, JEV_DEFAULT_MODEL, } from './vision/decisions.js';
7
8
  const KEY_ALIASES = {
8
9
  tab: 'Tab',
9
10
  enter: 'Enter',
@@ -89,6 +90,9 @@ export class TdSession {
89
90
  get visionCalls() {
90
91
  return this.engine.visionCalls;
91
92
  }
93
+ get cacheStats() {
94
+ return this.engine.cacheStats;
95
+ }
92
96
  /** Non-fatal anomalies observed by the engine — journaled as evidence. */
93
97
  get errorRecords() {
94
98
  return this.engine.errorRecords;
@@ -1,4 +1,5 @@
1
1
  import { FingerprintRecord } from './fingerprint.js';
2
+ export declare const FLOW_CACHE_SCHEMA_VERSION: 1;
2
3
  /** A cached assertion verdict, keyed on (question, page-state hash). */
3
4
  export interface CachedAssert {
4
5
  question: string;
@@ -9,6 +10,7 @@ export interface CachedAssert {
9
10
  model: string | undefined;
10
11
  }
11
12
  export interface FlowCache {
13
+ schemaVersion?: typeof FLOW_CACHE_SCHEMA_VERSION;
12
14
  steps: FingerprintRecord[];
13
15
  asserts?: CachedAssert[];
14
16
  }
@@ -1,6 +1,7 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { writeAtomicJson } from '../fsutil.js';
4
+ export const FLOW_CACHE_SCHEMA_VERSION = 1;
4
5
  function sortKeys(_, value) {
5
6
  if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
6
7
  const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
@@ -19,11 +20,17 @@ export async function loadFlow(cacheDir, flowName) {
19
20
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
20
21
  return undefined;
21
22
  }
22
- const { steps, asserts } = parsed;
23
+ const { schemaVersion, steps, asserts } = parsed;
23
24
  if (!Array.isArray(steps)) {
24
25
  return undefined;
25
26
  }
26
- const out = { steps: steps };
27
+ if (schemaVersion !== undefined && schemaVersion !== FLOW_CACHE_SCHEMA_VERSION) {
28
+ return undefined;
29
+ }
30
+ const out = {
31
+ ...(schemaVersion === FLOW_CACHE_SCHEMA_VERSION ? { schemaVersion } : {}),
32
+ steps: steps,
33
+ };
27
34
  if (Array.isArray(asserts))
28
35
  out.asserts = asserts;
29
36
  return out;
@@ -37,5 +44,5 @@ export async function loadFlow(cacheDir, flowName) {
37
44
  }
38
45
  }
39
46
  export async function saveFlow(cacheDir, flowName, steps, asserts) {
40
- await writeAtomicJson(flowPath(cacheDir, flowName), { steps, asserts: asserts ?? [] }, sortKeys);
47
+ await writeAtomicJson(flowPath(cacheDir, flowName), { schemaVersion: FLOW_CACHE_SCHEMA_VERSION, steps, asserts: asserts ?? [] }, sortKeys);
41
48
  }
package/dist/cli.d.ts CHANGED
@@ -3,6 +3,14 @@ import { Config } from './config.js';
3
3
  import { type ExecFn } from './detect.js';
4
4
  import { BrowserDriver } from './driver/browser.js';
5
5
  import { VisionClient } from './engine/loop.js';
6
+ import { type PrMeta } from './evidence/ci.js';
7
+ import { type Evidence } from './evidence/link.js';
8
+ import { type SecretsScanResult } from './review/secrets.js';
9
+ import { type TriageRecord } from './review/triage.js';
10
+ import { type FindingAdjudicationAudit } from './review/adjudicate.js';
11
+ import { type ProbeRecord } from './probe/queue.js';
12
+ import { type HeadBinding } from './report/manifest.js';
13
+ import { CallCost } from './vision/cost.js';
6
14
  export interface CliDeps {
7
15
  cwd?: string;
8
16
  env?: NodeJS.ProcessEnv;
@@ -21,5 +29,134 @@ interface PrFile {
21
29
  previous_filename?: string;
22
30
  patch?: string;
23
31
  }
32
+ export interface ReviewFinding {
33
+ file: string;
34
+ line?: number;
35
+ severity: string;
36
+ category?: string;
37
+ message: string;
38
+ /** U8 — Jev true-positive probability (absent = unadjudicated). */
39
+ p?: number;
40
+ /** R1 — committable replacement lines for the commented range (parse-bounded). */
41
+ suggestion?: string;
42
+ /** R1 — first line of the replaced range; absent = single-line fix at `line`. */
43
+ startLine?: number;
44
+ evidence?: Evidence;
45
+ }
46
+ /** KTD3 — one pre-rendered inline review comment; posters POST it verbatim. */
47
+ export interface ReviewComment {
48
+ path: string;
49
+ line: number;
50
+ start_line?: number;
51
+ start_side?: 'RIGHT';
52
+ side: 'RIGHT';
53
+ body: string;
54
+ /** R10 — path:line:bodyFirstLine:hash8(suggestion); a corrected suggestion re-posts. */
55
+ dedupKey: string;
56
+ }
57
+ interface CodeReviewReport {
58
+ ok: boolean;
59
+ skipped: boolean;
60
+ summary: string;
61
+ verdict: 'pass' | 'needs_changes' | 'approve';
62
+ findings: ReviewFinding[];
63
+ /** Inline-comment cap consumed by the sticky poster (Tencent max_comments pull). */
64
+ maxComments?: number;
65
+ /** R3/KTD2 — poster gate: 'request_changes' only for proven blockers. */
66
+ reviewEvent: 'comment' | 'request_changes';
67
+ /** Blocker-severity findings a sandbox probe reproduced. */
68
+ provenBlockers: number;
69
+ /** Blocker-severity findings at/above the Jev P(true-positive) gate. */
70
+ highConfidenceBlockers: number;
71
+ /** KTD3 — eligibility-filtered, severity-sorted, sanitized, capped. */
72
+ reviewComments: ReviewComment[];
73
+ /** Eligible findings dropped by the maxComments cap. */
74
+ commentsOverflow: number;
75
+ /** B.2 probe audit records — present only when the sandbox lane ran. */
76
+ probes?: ProbeRecord[];
77
+ /** Why an enabled lane bowed out (fork gate, no docker, no harness…). */
78
+ probeLaneSkipped?: string;
79
+ /** Secrets-lane audit — masked candidates, adjudication verdicts, skip reason. */
80
+ secretsScan?: SecretsScanResult | {
81
+ skipped: string;
82
+ };
83
+ /** U7 triage record — Jev pre-review signals (annotate/route, never gates). */
84
+ triage?: TriageRecord;
85
+ /** U8 adjudication audit — per-finding p + suppressed records. */
86
+ findingAdjudication?: FindingAdjudicationAudit;
87
+ calls: CallCost[];
88
+ visionCostUsd: number;
89
+ tokens: number;
90
+ model: string;
91
+ budgetExceeded: boolean;
92
+ /** Identity relationship between the report source and checkout. */
93
+ headBinding?: HeadBinding;
94
+ }
95
+ /**
96
+ * Split `git diff` text into per-file PrFile entries — the local-diff
97
+ * equivalent of the PR-files API response (which also reports `patch`
98
+ * per file). `+++ b/` names new/copied files; `--- a/` covers deletions.
99
+ */
100
+ export declare function filesFromUnifiedDiff(diff: string): PrFile[];
101
+ /**
102
+ * `--fixture <dir>` seam: the dir is a real git repo with an
103
+ * `argus-fixture-base` ref (the merge base) and HEAD at the PR head —
104
+ * scripts/demo.mjs materializes it. Returns the same diff/files/meta
105
+ * the GitHub paths would produce, so every downstream lane (chunking,
106
+ * secrets scan, evidence linkage) runs its real code path.
107
+ */
108
+ export declare function loadFixture(dir: string, exec?: ExecFn): Promise<{
109
+ files: PrFile[];
110
+ meta: PrMeta;
111
+ diff: string;
112
+ } | {
113
+ skipped: string;
114
+ }>;
24
115
  export declare function buildPatchChunks(files: PrFile[], contexts?: Record<string, string>): string[];
116
+ export declare function parseCodeReview(content: string): {
117
+ summary: string;
118
+ verdict: 'pass' | 'needs_changes' | 'approve';
119
+ findings: CodeReviewReport['findings'];
120
+ };
121
+ /**
122
+ * KTD1 — a surviving synthesized finding's suggestion is restored verbatim
123
+ * from its pre-synthesis original, matched on file + line + whitespace-
124
+ * normalized message. With no pre-image the synthesized copy is dropped:
125
+ * synthesis output is ungrounded model text, never committable code.
126
+ */
127
+ export declare function carryForwardSuggestions(findings: CodeReviewReport['findings'], originals: CodeReviewReport['findings']): CodeReviewReport['findings'];
128
+ /**
129
+ * R3/KTD2 — Jev P(true-positive) at/above which a blocker-severity finding
130
+ * counts as proven for the REQUEST_CHANGES gate. This is a different axis
131
+ * from `review.findingThreshold` (P(false-positive) for nit/q suppression)
132
+ * — never reuse that knob. 0.7: high-confidence without demanding
133
+ * near-certainty from a calibrated scorer.
134
+ */
135
+ export declare const P_TRUE_POSITIVE_THRESHOLD = 0.7;
136
+ /**
137
+ * KTD2 — the poster-facing review gate, computed once at report assembly
138
+ * on linkedFindings (post-adjudication `p`, post-probe `evidence`,
139
+ * secrets-lane `pLive` already carried as `p`) and serialized into
140
+ * code-review.json; posters read `reviewEvent`, never recompute.
141
+ * Unadjudicated blockers (no p, not reproduced) never escalate —
142
+ * degrade-open by design. The two counts overlap deliberately: a
143
+ * reproduced AND Jev-confident finding is reported under both.
144
+ */
145
+ export declare function computeReviewEvent(findings: ReviewFinding[], blockSeverities: string[], allowRequestChanges: boolean): {
146
+ reviewEvent: 'comment' | 'request_changes';
147
+ provenBlockers: number;
148
+ highConfidenceBlockers: number;
149
+ };
150
+ /**
151
+ * KTD3 — pre-render the inline review surface: eligibility-filtered
152
+ * (R8's static half — real path, positive integer line), severity-sorted
153
+ * before the maxComments cap so nits can't crowd out bugs (R2), sanitized
154
+ * (R5), suggestion-fenced, each carrying a dedupKey (R10). Posters consume
155
+ * `comments` verbatim — dedup + live-diff validation + POST, no render
156
+ * policy. `overflow` is the count of eligible findings past the cap.
157
+ */
158
+ export declare function renderReviewComments(findings: ReviewFinding[], maxComments?: number): {
159
+ comments: ReviewComment[];
160
+ overflow: number;
161
+ };
25
162
  export {};