nexus-agents 2.91.0 → 2.92.1

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 (29) hide show
  1. package/dist/{chunk-W7YQOXCJ.js → chunk-74AO4RKT.js} +2 -2
  2. package/dist/{chunk-PLFKZUVT.js → chunk-G3CYK3WA.js} +7 -5
  3. package/dist/{chunk-PLFKZUVT.js.map → chunk-G3CYK3WA.js.map} +1 -1
  4. package/dist/{chunk-3RZWLQSC.js → chunk-G5XKEUAP.js} +74 -2
  5. package/dist/chunk-G5XKEUAP.js.map +1 -0
  6. package/dist/{chunk-6IV43POQ.js → chunk-L6JZCAFH.js} +96 -624
  7. package/dist/chunk-L6JZCAFH.js.map +1 -0
  8. package/dist/chunk-NR4NSTJH.js +547 -0
  9. package/dist/chunk-NR4NSTJH.js.map +1 -0
  10. package/dist/{chunk-X2M7OF27.js → chunk-OK6U5N5Y.js} +3 -1
  11. package/dist/chunk-OK6U5N5Y.js.map +1 -0
  12. package/dist/{chunk-DH4V6S5K.js → chunk-ZNR3ZVC6.js} +3 -3
  13. package/dist/cli.js +43 -20
  14. package/dist/cli.js.map +1 -1
  15. package/dist/index.d.ts +190 -190
  16. package/dist/index.js +19 -17
  17. package/dist/index.js.map +1 -1
  18. package/dist/{issue-triage-CDSHKFPP.js → issue-triage-FVZOVIRX.js} +3 -2
  19. package/dist/{pr-reviewer-helpers-WYPUYQ2U.js → pr-reviewer-helpers-RBUEJI6V.js} +14 -3
  20. package/dist/{setup-command-B5QPHCKY.js → setup-command-AZAWBZOP.js} +3 -3
  21. package/package.json +1 -1
  22. package/dist/chunk-3RZWLQSC.js.map +0 -1
  23. package/dist/chunk-6IV43POQ.js.map +0 -1
  24. package/dist/chunk-X2M7OF27.js.map +0 -1
  25. /package/dist/{chunk-W7YQOXCJ.js.map → chunk-74AO4RKT.js.map} +0 -0
  26. /package/dist/{chunk-DH4V6S5K.js.map → chunk-ZNR3ZVC6.js.map} +0 -0
  27. /package/dist/{issue-triage-CDSHKFPP.js.map → issue-triage-FVZOVIRX.js.map} +0 -0
  28. /package/dist/{pr-reviewer-helpers-WYPUYQ2U.js.map → pr-reviewer-helpers-RBUEJI6V.js.map} +0 -0
  29. /package/dist/{setup-command-B5QPHCKY.js.map → setup-command-AZAWBZOP.js.map} +0 -0
@@ -7,6 +7,7 @@ var DEFAULT_PR_REVIEW_CONFIG = {
7
7
  minSeverity: "low",
8
8
  enableInlineComments: true,
9
9
  dryRun: false,
10
+ enableReputation: true,
10
11
  modelConfig: {
11
12
  temperature: 0.3,
12
13
  maxTokens: 8192
@@ -28,6 +29,7 @@ var PRReviewConfigSchema = z.object({
28
29
  minSeverity: z.enum(["critical", "high", "medium", "low", "info"]).default("low"),
29
30
  enableInlineComments: z.boolean().default(true),
30
31
  dryRun: z.boolean().default(false),
32
+ enableReputation: z.boolean().default(true),
31
33
  githubToken: z.string().optional(),
32
34
  modelConfig: z.object({
33
35
  temperature: z.number().min(0).max(2).default(0.3),
@@ -69,4 +71,4 @@ export {
69
71
  SEVERITY_EMOJI,
70
72
  DECISION_EMOJI
71
73
  };
72
- //# sourceMappingURL=chunk-X2M7OF27.js.map
74
+ //# sourceMappingURL=chunk-OK6U5N5Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dogfooding/pr-review-types.ts"],"sourcesContent":["/**\n * nexus-agents/dogfooding - PR Review Types\n *\n * Type definitions for automated pull request review using\n * multi-agent collaboration.\n *\n * @module dogfooding/pr-review-types\n * (Source: Issue #161, Alignment Roadmap Phase 3)\n */\n\nimport { z } from 'zod';\nimport type { FullCapableProvider } from '../scm/types.js';\n\n/**\n * Pull request file change information.\n */\nexport interface PRFileChange {\n /** File path */\n readonly filename: string;\n /** Change status */\n readonly status: 'added' | 'removed' | 'modified' | 'renamed' | 'copied';\n /** Number of additions */\n readonly additions: number;\n /** Number of deletions */\n readonly deletions: number;\n /** File patch/diff content */\n readonly patch?: string | undefined;\n /** Previous filename if renamed */\n readonly previousFilename?: string | undefined;\n}\n\n/**\n * Pull request metadata from GitHub API.\n */\nexport interface PRMetadata {\n /** PR number */\n readonly number: number;\n /** PR title */\n readonly title: string;\n /** PR description/body */\n readonly body: string;\n /** Author username */\n readonly author: string;\n /** GitHub API author_association (e.g., 'OWNER', 'COLLABORATOR', 'NONE') */\n readonly authorAssociation: string;\n /** Base branch */\n readonly base: string;\n /** Head branch */\n readonly head: string;\n /** Repository owner */\n readonly owner: string;\n /** Repository name */\n readonly repo: string;\n /** PR URL */\n readonly url: string;\n /** Whether PR is draft */\n readonly draft: boolean;\n /** PR labels */\n readonly labels: readonly string[];\n /** Changed files */\n readonly files: readonly PRFileChange[];\n /** Total additions */\n readonly additions: number;\n /** Total deletions */\n readonly deletions: number;\n /** Commit SHA of head */\n readonly headSha: string;\n}\n\n/**\n * Review severity levels.\n */\nexport type ReviewSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';\n\n/**\n * Review category for organizing findings.\n */\nexport type ReviewCategory =\n | 'security'\n | 'performance'\n | 'code_quality'\n | 'testing'\n | 'documentation'\n | 'architecture';\n\n/**\n * Individual review finding from an expert.\n */\nexport interface ReviewFinding {\n /** Unique finding ID */\n readonly id: string;\n /** Category of finding */\n readonly category: ReviewCategory;\n /** Severity level */\n readonly severity: ReviewSeverity;\n /** Finding title */\n readonly title: string;\n /** Detailed description */\n readonly description: string;\n /** Affected file (if applicable) */\n readonly file?: string | undefined;\n /** Line number (if applicable) */\n readonly line?: number | undefined;\n /** Suggested fix */\n readonly suggestion?: string | undefined;\n /** Expert that found this issue */\n readonly expertId: string;\n /** Confidence score (0-1) */\n readonly confidence: number;\n}\n\n/**\n * Review result from a single expert agent.\n */\nexport interface ExpertReviewResult {\n /** Expert ID */\n readonly expertId: string;\n /** Expert role/type */\n readonly expertType: string;\n /** Overall approval */\n readonly approved: boolean;\n /** Review summary */\n readonly summary: string;\n /** Individual findings */\n readonly findings: readonly ReviewFinding[];\n /** Execution time in ms */\n readonly durationMs: number;\n /** Confidence in review (0-1) */\n readonly confidence: number;\n}\n\n/**\n * Aggregated review decision after multi-agent debate.\n */\nexport type ReviewDecision = 'approve' | 'request_changes' | 'comment';\n\n/**\n * Complete PR review result from multi-agent collaboration.\n */\n/**\n * Trust + reputation assessment of the PR author, surfaced for observability\n * (#3123, epic #3118 Phase 5). Mirrors `issue_triage`'s assessment.\n */\nexport interface PRTrustAssessment {\n /** Classifier trust tier (1-4) from author association. */\n readonly trustTier: string;\n /** Author's GitHub role. */\n readonly userRole: string;\n /** Whether the author is on the maintainer allowlist. */\n readonly isAllowlisted: boolean;\n /** Reputation score (0-100) when reputation is enabled. */\n readonly reputationScore?: number | undefined;\n /** Suspicious signals detected (e.g. `new_account`, `injection_patterns_detected`). */\n readonly suspiciousSignals: readonly string[];\n /** Whether the author is flagged as suspicious. */\n readonly isSuspicious: boolean;\n /** Tier the policy gate ACTUALLY enforced (== trustTier under audit/off). */\n readonly enforcedTrustTier?: string | undefined;\n /** Tier reputation reconciliation computed — what enforce mode WOULD gate on. */\n readonly reputationReconciledTier?: string | undefined;\n /** Reputation-gating rollout mode applied: `off` | `audit` | `enforce`. */\n readonly gatingMode?: string | undefined;\n}\n\n/**\n * Result of fetching PR data plus best-effort author signals (#3133).\n */\nexport interface PRFetchData {\n readonly metadata: PRMetadata;\n readonly provider: FullCapableProvider;\n /** Author's real account age in days, when the lookup succeeded (#3133). */\n readonly accountAgeDays?: number;\n}\n\nexport interface PRReviewResult {\n /** Pull request number */\n readonly prNumber: number;\n /** Repository full name (owner/repo) */\n readonly repository: string;\n /** Overall decision */\n readonly decision: ReviewDecision;\n /** Executive summary */\n readonly summary: string;\n /** Individual expert reviews */\n readonly expertReviews: readonly ExpertReviewResult[];\n /** Aggregated findings by severity */\n readonly findingsBySeverity: Readonly<Record<ReviewSeverity, number>>;\n /** Aggregated findings by category */\n readonly findingsByCategory: Readonly<Record<ReviewCategory, number>>;\n /** Total execution time in ms */\n readonly totalDurationMs: number;\n /** Number of experts that participated */\n readonly expertCount: number;\n /** Consensus score (0-1) */\n readonly consensusScore: number;\n /** Debate rounds completed */\n readonly debateRounds: number;\n /** Timestamp of review */\n readonly timestamp: string;\n /** Author trust + reputation assessment (#3123). */\n readonly trustAssessment: PRTrustAssessment;\n}\n\n/**\n * Configuration for PR review.\n */\nexport interface PRReviewConfig {\n /** Experts to include in review */\n readonly experts: readonly ReviewCategory[];\n /** Maximum debate rounds */\n readonly maxDebateRounds: number;\n /** Consensus threshold (0-1) */\n readonly consensusThreshold: number;\n /** Minimum severity to report */\n readonly minSeverity: ReviewSeverity;\n /** Whether to post inline comments */\n readonly enableInlineComments: boolean;\n /** Whether to run in dry-run mode (no GitHub posting) */\n readonly dryRun: boolean;\n /** Whether to assess author reputation and gate on it (#3123). */\n readonly enableReputation: boolean;\n /** GitHub token for API access */\n readonly githubToken?: string | undefined;\n /** Model adapter configuration */\n readonly modelConfig?: {\n readonly temperature?: number;\n readonly maxTokens?: number;\n };\n}\n\n/**\n * Default PR review configuration.\n */\nexport const DEFAULT_PR_REVIEW_CONFIG: PRReviewConfig = {\n experts: ['security', 'code_quality', 'testing'],\n maxDebateRounds: 3,\n consensusThreshold: 0.7,\n minSeverity: 'low',\n enableInlineComments: true,\n dryRun: false,\n enableReputation: true,\n modelConfig: {\n temperature: 0.3,\n maxTokens: 8192,\n },\n};\n\n/**\n * Zod schema for PR review configuration.\n */\nexport const PRReviewConfigSchema = z.object({\n experts: z\n .array(\n z.enum([\n 'security',\n 'performance',\n 'code_quality',\n 'testing',\n 'documentation',\n 'architecture',\n ])\n )\n .default(['security', 'code_quality', 'testing']),\n maxDebateRounds: z.number().int().min(1).max(10).default(3),\n consensusThreshold: z.number().min(0).max(1).default(0.7),\n minSeverity: z.enum(['critical', 'high', 'medium', 'low', 'info']).default('low'),\n enableInlineComments: z.boolean().default(true),\n dryRun: z.boolean().default(false),\n enableReputation: z.boolean().default(true),\n githubToken: z.string().optional(),\n modelConfig: z\n .object({\n temperature: z.number().min(0).max(2).default(0.3),\n maxTokens: z.number().int().positive().default(8192),\n })\n .optional(),\n});\n\n/**\n * Severity order for comparison.\n */\nexport const SEVERITY_ORDER: Record<ReviewSeverity, number> = {\n critical: 5,\n high: 4,\n medium: 3,\n low: 2,\n info: 1,\n};\n\n/**\n * Category display names.\n */\nexport const CATEGORY_DISPLAY_NAMES: Record<ReviewCategory, string> = {\n security: 'Security',\n performance: 'Performance',\n code_quality: 'Code Quality',\n testing: 'Testing',\n documentation: 'Documentation',\n architecture: 'Architecture',\n};\n\n/**\n * Severity emoji for GitHub comments.\n */\nexport const SEVERITY_EMOJI: Record<ReviewSeverity, string> = {\n critical: ':rotating_light:',\n high: ':warning:',\n medium: ':yellow_circle:',\n low: ':large_blue_circle:',\n info: ':information_source:',\n};\n\n/**\n * Decision emoji for GitHub comments.\n */\nexport const DECISION_EMOJI: Record<ReviewDecision, string> = {\n approve: ':white_check_mark:',\n request_changes: ':x:',\n comment: ':speech_balloon:',\n};\n"],"mappings":";AAUA,SAAS,SAAS;AA+NX,IAAM,2BAA2C;AAAA,EACtD,SAAS,CAAC,YAAY,gBAAgB,SAAS;AAAA,EAC/C,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,aAAa;AAAA,EACb,sBAAsB;AAAA,EACtB,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB,aAAa;AAAA,IACX,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AACF;AAKO,IAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,SAAS,EACN;AAAA,IACC,EAAE,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,EACC,QAAQ,CAAC,YAAY,gBAAgB,SAAS,CAAC;AAAA,EAClD,iBAAiB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC;AAAA,EAC1D,oBAAoB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG;AAAA,EACxD,aAAa,EAAE,KAAK,CAAC,YAAY,QAAQ,UAAU,OAAO,MAAM,CAAC,EAAE,QAAQ,KAAK;AAAA,EAChF,sBAAsB,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EAC9C,QAAQ,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EACjC,kBAAkB,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EAC1C,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,aAAa,EACV,OAAO;AAAA,IACN,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG;AAAA,IACjD,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,IAAI;AAAA,EACrD,CAAC,EACA,SAAS;AACd,CAAC;AAKM,IAAM,iBAAiD;AAAA,EAC5D,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,MAAM;AACR;AAKO,IAAM,yBAAyD;AAAA,EACpE,UAAU;AAAA,EACV,aAAa;AAAA,EACb,cAAc;AAAA,EACd,SAAS;AAAA,EACT,eAAe;AAAA,EACf,cAAc;AAChB;AAKO,IAAM,iBAAiD;AAAA,EAC5D,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,MAAM;AACR;AAKO,IAAM,iBAAiD;AAAA,EAC5D,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,SAAS;AACX;","names":[]}
@@ -40,7 +40,7 @@ import {
40
40
  } from "./chunk-CH7QIDHQ.js";
41
41
 
42
42
  // src/version.ts
43
- var VERSION = true ? "2.91.0" : "dev";
43
+ var VERSION = true ? "2.92.1" : "dev";
44
44
 
45
45
  // src/config/schemas-core.ts
46
46
  import { z } from "zod";
@@ -2096,7 +2096,7 @@ async function runDoctorFix(result) {
2096
2096
  writeLine2("\u2500".repeat(40));
2097
2097
  let fixCount = 0;
2098
2098
  if (!result.dataDirectory.rootExists || result.dataDirectory.subdirectories.some((d) => !d.exists || !d.writable)) {
2099
- const { runSetup } = await import("./setup-command-B5QPHCKY.js");
2099
+ const { runSetup } = await import("./setup-command-AZAWBZOP.js");
2100
2100
  const setupResult = runSetup({
2101
2101
  skipMcp: true,
2102
2102
  skipRules: true,
@@ -2208,4 +2208,4 @@ export {
2208
2208
  startStdioServer,
2209
2209
  closeServer
2210
2210
  };
2211
- //# sourceMappingURL=chunk-DH4V6S5K.js.map
2211
+ //# sourceMappingURL=chunk-ZNR3ZVC6.js.map
package/dist/cli.js CHANGED
@@ -7,11 +7,13 @@ import {
7
7
  determineApproval,
8
8
  determineDecision,
9
9
  extractSummary,
10
+ fetchAccountAgeDays,
10
11
  formatReviewComment,
12
+ gatePRAuthor,
11
13
  generateSummary,
12
14
  parseFindings,
13
15
  sumFindings
14
- } from "./chunk-3RZWLQSC.js";
16
+ } from "./chunk-G5XKEUAP.js";
15
17
  import "./chunk-AQTHLUCP.js";
16
18
  import {
17
19
  buildOpenAICompatAdapters,
@@ -22,7 +24,7 @@ import {
22
24
  import {
23
25
  setupCommandAsync,
24
26
  verifyCommand
25
- } from "./chunk-W7YQOXCJ.js";
27
+ } from "./chunk-74AO4RKT.js";
26
28
  import "./chunk-2SMTCNXP.js";
27
29
  import {
28
30
  AuthHandler,
@@ -162,14 +164,14 @@ import {
162
164
  validateCommand,
163
165
  validateWorkflow,
164
166
  wrapInMarkdownFence
165
- } from "./chunk-PLFKZUVT.js";
167
+ } from "./chunk-G3CYK3WA.js";
166
168
  import {
167
169
  resolveToken
168
170
  } from "./chunk-EAT4GTMT.js";
169
171
  import {
170
172
  CATEGORY_DISPLAY_NAMES,
171
173
  DEFAULT_PR_REVIEW_CONFIG
172
- } from "./chunk-X2M7OF27.js";
174
+ } from "./chunk-OK6U5N5Y.js";
173
175
  import "./chunk-E6W4Z6GN.js";
174
176
  import "./chunk-3LKW4IEW.js";
175
177
  import {
@@ -205,10 +207,13 @@ import {
205
207
  classifyTrust,
206
208
  createFullGitHubProvider,
207
209
  evaluatePolicy,
208
- parsePRUrl,
209
- sanitizeInput
210
- } from "./chunk-6IV43POQ.js";
210
+ parsePRUrl
211
+ } from "./chunk-L6JZCAFH.js";
211
212
  import "./chunk-EFHZHCF2.js";
213
+ import {
214
+ ReputationCache,
215
+ sanitizeInput
216
+ } from "./chunk-NR4NSTJH.js";
212
217
  import "./chunk-57HMLBYN.js";
213
218
  import "./chunk-HFOQKCD2.js";
214
219
  import "./chunk-3ACDP4E6.js";
@@ -235,7 +240,7 @@ import {
235
240
  loadConfig,
236
241
  runDoctor,
237
242
  validateNexusEnv
238
- } from "./chunk-DH4V6S5K.js";
243
+ } from "./chunk-ZNR3ZVC6.js";
239
244
  import "./chunk-SFZESA5O.js";
240
245
  import "./chunk-6Q33PBXC.js";
241
246
  import {
@@ -2840,10 +2845,12 @@ var PRReviewer = class {
2840
2845
  config;
2841
2846
  observer;
2842
2847
  adapter;
2848
+ reputationCache;
2843
2849
  constructor(config, adapter) {
2844
2850
  this.config = { ...DEFAULT_PR_REVIEW_CONFIG, ...config };
2845
2851
  this.observer = new SwarmObserver();
2846
2852
  this.adapter = adapter ?? void 0;
2853
+ this.reputationCache = new ReputationCache();
2847
2854
  }
2848
2855
  /**
2849
2856
  * Reviews a pull request using multi-agent collaboration.
@@ -2857,7 +2864,7 @@ var PRReviewer = class {
2857
2864
  const { owner, repo, prNumber } = parseResult.value;
2858
2865
  const fetchResult = await this.fetchPRData(owner, repo, prNumber);
2859
2866
  if (!fetchResult.ok) return fetchResult;
2860
- const { metadata: prMetadata, provider } = fetchResult.value;
2867
+ const { metadata: prMetadata, provider, accountAgeDays } = fetchResult.value;
2861
2868
  const trustResult = this.classifyPRAuthor(prMetadata);
2862
2869
  logger2.info("PR author trust classified", {
2863
2870
  prNumber,
@@ -2866,10 +2873,17 @@ var PRReviewer = class {
2866
2873
  userRole: trustResult.userRole,
2867
2874
  isAllowlisted: trustResult.isAllowlisted
2868
2875
  });
2876
+ const { gateDecision, trustAssessment } = gatePRAuthor(
2877
+ prMetadata,
2878
+ trustResult,
2879
+ accountAgeDays,
2880
+ this.reputationCache,
2881
+ this.config.enableReputation
2882
+ );
2869
2883
  const expertReviews = await this.runExpertReviews(prMetadata, traceId);
2870
- const result = this.aggregateReviews(prMetadata, expertReviews, startTime);
2884
+ const result = this.aggregateReviews(prMetadata, expertReviews, startTime, trustAssessment);
2871
2885
  if (!this.config.dryRun) {
2872
- await this.postReviewToGitHub(provider, parseResult.value, result, trustResult);
2886
+ await this.postReviewToGitHub(provider, parseResult.value, result, gateDecision);
2873
2887
  }
2874
2888
  logger2.info("PR review completed", {
2875
2889
  prNumber,
@@ -3053,7 +3067,7 @@ ${file.patch}
3053
3067
  /**
3054
3068
  * Aggregates individual expert reviews into final result.
3055
3069
  */
3056
- aggregateReviews(pr, reviews, startTime) {
3070
+ aggregateReviews(pr, reviews, startTime, trustAssessment) {
3057
3071
  const allFindings = reviews.flatMap((r) => r.findings);
3058
3072
  const decision = determineDecision(reviews, allFindings);
3059
3073
  const consensusScore = calculateConsensus(reviews);
@@ -3069,7 +3083,8 @@ ${file.patch}
3069
3083
  expertCount: reviews.length,
3070
3084
  consensusScore,
3071
3085
  debateRounds: 1,
3072
- timestamp: getTimeProvider().nowIso()
3086
+ timestamp: getTimeProvider().nowIso(),
3087
+ trustAssessment
3073
3088
  };
3074
3089
  }
3075
3090
  /**
@@ -3079,8 +3094,8 @@ ${file.patch}
3079
3094
  * from the untrusted PR author.
3080
3095
  * (Source: Issue #828 — Wire policy gate into production pipeline)
3081
3096
  */
3082
- async postReviewToGitHub(provider, pr, result, trustResult) {
3083
- const policyResult = this.auditReviewAction(trustResult);
3097
+ async postReviewToGitHub(provider, pr, result, gateDecision) {
3098
+ const policyResult = this.auditReviewAction(gateDecision.enforcedTier);
3084
3099
  if (policyResult.hasRuleOfTwoViolation) {
3085
3100
  logger2.warn("Rule of Two: review posting blocked", {
3086
3101
  prNumber: pr.prNumber,
@@ -3094,17 +3109,20 @@ ${file.patch}
3094
3109
  violations: policyResult.violations
3095
3110
  });
3096
3111
  }
3097
- const { formatReviewComment: formatReviewComment2 } = await import("./pr-reviewer-helpers-WYPUYQ2U.js");
3112
+ const { formatReviewComment: formatReviewComment2 } = await import("./pr-reviewer-helpers-RBUEJI6V.js");
3098
3113
  const body = formatReviewComment2(result);
3099
3114
  const postResult = await provider.createReview(pr.prNumber, body, result.decision);
3100
3115
  if (!postResult.ok) {
3101
3116
  logger2.error("Failed to post review", postResult.error);
3102
3117
  }
3103
3118
  }
3104
- /** Audits review posting against the policy gate for logging. */
3105
- auditReviewAction(trustResult) {
3119
+ /**
3120
+ * Audits review posting against the policy gate. Gates on the reputation-
3121
+ * reconciled `enforcedTier` (#3123) rather than the raw classifier tier.
3122
+ */
3123
+ auditReviewAction(enforcedTier) {
3106
3124
  const context = {
3107
- inputTrustTier: trustResult.trustTier,
3125
+ inputTrustTier: enforcedTier,
3108
3126
  hasWriteAccess: true,
3109
3127
  hasSecretAccess: true
3110
3128
  };
@@ -3135,6 +3153,7 @@ ${file.patch}
3135
3153
  const detailResult = await provider.getPullRequestDetail(prNumber);
3136
3154
  if (!detailResult.ok) return err(detailResult.error);
3137
3155
  const detail = detailResult.value;
3156
+ const accountAgeDays = this.config.enableReputation ? await fetchAccountAgeDays(provider, detail.author) : void 0;
3138
3157
  const metadata = {
3139
3158
  number: detail.number,
3140
3159
  title: detail.title,
@@ -3160,7 +3179,11 @@ ${file.patch}
3160
3179
  additions: detail.additions,
3161
3180
  deletions: detail.deletions
3162
3181
  };
3163
- return ok({ metadata, provider });
3182
+ return ok({
3183
+ metadata,
3184
+ provider,
3185
+ ...accountAgeDays !== void 0 ? { accountAgeDays } : {}
3186
+ });
3164
3187
  }
3165
3188
  /**
3166
3189
  * Gets the SwarmObserver for debugging.