@humanbased/crosscheck 1.2.0-beta.80 → 1.2.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/LICENSE +1 -1
- package/README.md +388 -149
- package/README.zh.md +1 -1
- package/crosscheck.config.example.yml +2 -63
- package/dist/__tests__/codex.test.js +27 -14
- package/dist/__tests__/codex.test.js.map +1 -1
- package/dist/__tests__/comment-bodies.test.js +1 -49
- package/dist/__tests__/comment-bodies.test.js.map +1 -1
- package/dist/__tests__/conflict-resolve.test.js +1 -44
- package/dist/__tests__/conflict-resolve.test.js.map +1 -1
- package/dist/__tests__/fix.test.js +0 -33
- package/dist/__tests__/fix.test.js.map +1 -1
- package/dist/__tests__/onboard-preservation.test.js +0 -5
- package/dist/__tests__/onboard-preservation.test.js.map +1 -1
- package/dist/__tests__/optimize.test.js +0 -2
- package/dist/__tests__/optimize.test.js.map +1 -1
- package/dist/__tests__/repo-picker.test.js +1 -7
- package/dist/__tests__/repo-picker.test.js.map +1 -1
- package/dist/__tests__/review-comment-body.test.js +0 -35
- package/dist/__tests__/review-comment-body.test.js.map +1 -1
- package/dist/__tests__/review-models.test.js +3 -19
- package/dist/__tests__/review-models.test.js.map +1 -1
- package/dist/__tests__/runner.test.js +1 -29
- package/dist/__tests__/runner.test.js.map +1 -1
- package/dist/cli.js +0 -29
- package/dist/cli.js.map +1 -1
- package/dist/commands/onboard.d.ts +0 -11
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +2 -88
- package/dist/commands/onboard.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +6 -65
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +3 -23
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +1 -53
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +6 -52
- package/dist/commands/watch.js.map +1 -1
- package/dist/config/loader.d.ts +1 -3
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +0 -13
- package/dist/config/loader.js.map +1 -1
- package/dist/config/review-model-tiers.json +3 -3
- package/dist/config/schema.d.ts +10 -254
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +2 -62
- package/dist/config/schema.js.map +1 -1
- package/dist/github/client.d.ts +1 -6
- package/dist/github/client.d.ts.map +1 -1
- package/dist/github/client.js +7 -15
- package/dist/github/client.js.map +1 -1
- package/dist/issues/ticket-ref.d.ts.map +1 -1
- package/dist/issues/ticket-ref.js +5 -6
- package/dist/issues/ticket-ref.js.map +1 -1
- package/dist/lib/clone.d.ts +0 -1
- package/dist/lib/clone.d.ts.map +1 -1
- package/dist/lib/clone.js +10 -32
- package/dist/lib/clone.js.map +1 -1
- package/dist/lib/comment-bodies.d.ts +0 -35
- package/dist/lib/comment-bodies.d.ts.map +1 -1
- package/dist/lib/comment-bodies.js +9 -43
- package/dist/lib/comment-bodies.js.map +1 -1
- package/dist/lib/repo-picker.d.ts +0 -3
- package/dist/lib/repo-picker.d.ts.map +1 -1
- package/dist/lib/repo-picker.js +9 -45
- package/dist/lib/repo-picker.js.map +1 -1
- package/dist/lib/review-models.d.ts +2 -15
- package/dist/lib/review-models.d.ts.map +1 -1
- package/dist/lib/review-models.js +6 -26
- package/dist/lib/review-models.js.map +1 -1
- package/dist/lib/runner.d.ts +0 -12
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +42 -186
- package/dist/lib/runner.js.map +1 -1
- package/dist/lib/workflow.d.ts +0 -9
- package/dist/lib/workflow.d.ts.map +1 -1
- package/dist/lib/workflow.js +0 -20
- package/dist/lib/workflow.js.map +1 -1
- package/dist/reviewers/claude.d.ts +1 -4
- package/dist/reviewers/claude.d.ts.map +1 -1
- package/dist/reviewers/claude.js +7 -39
- package/dist/reviewers/claude.js.map +1 -1
- package/dist/reviewers/codex.d.ts +1 -3
- package/dist/reviewers/codex.d.ts.map +1 -1
- package/dist/reviewers/codex.js +70 -76
- package/dist/reviewers/codex.js.map +1 -1
- package/dist/reviewers/conflict-resolve.d.ts +1 -3
- package/dist/reviewers/conflict-resolve.d.ts.map +1 -1
- package/dist/reviewers/conflict-resolve.js +6 -21
- package/dist/reviewers/conflict-resolve.js.map +1 -1
- package/dist/reviewers/fix.d.ts +2 -5
- package/dist/reviewers/fix.d.ts.map +1 -1
- package/dist/reviewers/fix.js +10 -26
- package/dist/reviewers/fix.js.map +1 -1
- package/get-started.md +4 -139
- package/package.json +3 -4
- package/assets/icon-256.png +0 -0
- package/assets/linear-comment.svg +0 -18
- package/assets/linear-onboard.svg +0 -30
- package/assets/linear-status.svg +0 -23
- package/assets/linear-test.svg +0 -34
- package/assets/skills/code-review/.crosscheck-skill.json +0 -9
- package/assets/skills/code-review/LICENSE +0 -21
- package/assets/skills/code-review/SKILL.md +0 -89
- package/assets/skills/code-review/agents/openai.yaml +0 -3
- package/assets/skills/code-review-skill/.crosscheck-skill.json +0 -9
- package/assets/skills/code-review-skill/LICENSE +0 -21
- package/assets/skills/code-review-skill/SKILL.md +0 -231
- package/assets/skills/code-review-skill/assets/pr-review-template.md +0 -137
- package/assets/skills/code-review-skill/assets/review-checklist.md +0 -123
- package/assets/skills/code-review-skill/reference/angular.md +0 -768
- package/assets/skills/code-review-skill/reference/architecture-review-guide.md +0 -472
- package/assets/skills/code-review-skill/reference/c.md +0 -890
- package/assets/skills/code-review-skill/reference/code-quality-universal.md +0 -488
- package/assets/skills/code-review-skill/reference/code-review-best-practices.md +0 -136
- package/assets/skills/code-review-skill/reference/common-bugs-checklist.md +0 -286
- package/assets/skills/code-review-skill/reference/cpp.md +0 -893
- package/assets/skills/code-review-skill/reference/cross-cutting/async-concurrency-patterns.md +0 -515
- package/assets/skills/code-review-skill/reference/cross-cutting/error-handling-principles.md +0 -492
- package/assets/skills/code-review-skill/reference/cross-cutting/n-plus-one-queries.md +0 -309
- package/assets/skills/code-review-skill/reference/cross-cutting/sql-injection-prevention.md +0 -308
- package/assets/skills/code-review-skill/reference/cross-cutting/xss-prevention.md +0 -264
- package/assets/skills/code-review-skill/reference/csharp.md +0 -525
- package/assets/skills/code-review-skill/reference/css-less-sass.md +0 -661
- package/assets/skills/code-review-skill/reference/django.md +0 -985
- package/assets/skills/code-review-skill/reference/fastapi.md +0 -580
- package/assets/skills/code-review-skill/reference/go.md +0 -993
- package/assets/skills/code-review-skill/reference/java.md +0 -409
- package/assets/skills/code-review-skill/reference/java8.md +0 -586
- package/assets/skills/code-review-skill/reference/kotlin.md +0 -1018
- package/assets/skills/code-review-skill/reference/nestjs.md +0 -593
- package/assets/skills/code-review-skill/reference/performance-review-guide.md +0 -816
- package/assets/skills/code-review-skill/reference/php.md +0 -684
- package/assets/skills/code-review-skill/reference/python.md +0 -1073
- package/assets/skills/code-review-skill/reference/qt.md +0 -757
- package/assets/skills/code-review-skill/reference/react.md +0 -871
- package/assets/skills/code-review-skill/reference/ruby.md +0 -964
- package/assets/skills/code-review-skill/reference/rust.md +0 -846
- package/assets/skills/code-review-skill/reference/security-review-guide.md +0 -494
- package/assets/skills/code-review-skill/reference/svelte.md +0 -1064
- package/assets/skills/code-review-skill/reference/swift.md +0 -936
- package/assets/skills/code-review-skill/reference/typescript.md +0 -1016
- package/assets/skills/code-review-skill/reference/vue.md +0 -924
- package/assets/skills/code-review-skill/reference/zig.md +0 -440
- package/assets/skills/code-review-skill/scripts/pr-analyzer.py +0 -435
- package/assets/skills/code-review-skill/scripts/test_pr_analyzer.py +0 -380
- package/assets/skills/codebase-design/.crosscheck-skill.json +0 -9
- package/assets/skills/codebase-design/DEEPENING.md +0 -37
- package/assets/skills/codebase-design/DESIGN-IT-TWICE.md +0 -44
- package/assets/skills/codebase-design/LICENSE +0 -21
- package/assets/skills/codebase-design/SKILL.md +0 -114
- package/assets/skills/codebase-design/agents/openai.yaml +0 -3
- package/assets/skills/diagnosing-bugs/.crosscheck-skill.json +0 -9
- package/assets/skills/diagnosing-bugs/LICENSE +0 -21
- package/assets/skills/diagnosing-bugs/SKILL.md +0 -134
- package/assets/skills/diagnosing-bugs/agents/openai.yaml +0 -3
- package/assets/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +0 -41
- package/dist/__tests__/can-write-verdict.test.d.ts +0 -2
- package/dist/__tests__/can-write-verdict.test.d.ts.map +0 -1
- package/dist/__tests__/can-write-verdict.test.js +0 -31
- package/dist/__tests__/can-write-verdict.test.js.map +0 -1
- package/dist/__tests__/linear-branding.test.d.ts +0 -2
- package/dist/__tests__/linear-branding.test.d.ts.map +0 -1
- package/dist/__tests__/linear-branding.test.js +0 -156
- package/dist/__tests__/linear-branding.test.js.map +0 -1
- package/dist/__tests__/linear-client.test.d.ts +0 -2
- package/dist/__tests__/linear-client.test.d.ts.map +0 -1
- package/dist/__tests__/linear-client.test.js +0 -120
- package/dist/__tests__/linear-client.test.js.map +0 -1
- package/dist/__tests__/linear-comment.test.d.ts +0 -2
- package/dist/__tests__/linear-comment.test.d.ts.map +0 -1
- package/dist/__tests__/linear-comment.test.js +0 -151
- package/dist/__tests__/linear-comment.test.js.map +0 -1
- package/dist/__tests__/linear-identity.test.d.ts +0 -2
- package/dist/__tests__/linear-identity.test.d.ts.map +0 -1
- package/dist/__tests__/linear-identity.test.js +0 -253
- package/dist/__tests__/linear-identity.test.js.map +0 -1
- package/dist/__tests__/linear-notify.test.d.ts +0 -2
- package/dist/__tests__/linear-notify.test.d.ts.map +0 -1
- package/dist/__tests__/linear-notify.test.js +0 -144
- package/dist/__tests__/linear-notify.test.js.map +0 -1
- package/dist/__tests__/linear-ref.test.d.ts +0 -2
- package/dist/__tests__/linear-ref.test.d.ts.map +0 -1
- package/dist/__tests__/linear-ref.test.js +0 -261
- package/dist/__tests__/linear-ref.test.js.map +0 -1
- package/dist/__tests__/linear-test-ref.test.d.ts +0 -2
- package/dist/__tests__/linear-test-ref.test.d.ts.map +0 -1
- package/dist/__tests__/linear-test-ref.test.js +0 -81
- package/dist/__tests__/linear-test-ref.test.js.map +0 -1
- package/dist/__tests__/linear-verify.test.d.ts +0 -2
- package/dist/__tests__/linear-verify.test.d.ts.map +0 -1
- package/dist/__tests__/linear-verify.test.js +0 -132
- package/dist/__tests__/linear-verify.test.js.map +0 -1
- package/dist/__tests__/linear-worker.test.d.ts +0 -2
- package/dist/__tests__/linear-worker.test.d.ts.map +0 -1
- package/dist/__tests__/linear-worker.test.js +0 -83
- package/dist/__tests__/linear-worker.test.js.map +0 -1
- package/dist/__tests__/linear-write-possible.test.d.ts +0 -2
- package/dist/__tests__/linear-write-possible.test.d.ts.map +0 -1
- package/dist/__tests__/linear-write-possible.test.js +0 -30
- package/dist/__tests__/linear-write-possible.test.js.map +0 -1
- package/dist/__tests__/repository-guidance.test.d.ts +0 -2
- package/dist/__tests__/repository-guidance.test.d.ts.map +0 -1
- package/dist/__tests__/repository-guidance.test.js +0 -107
- package/dist/__tests__/repository-guidance.test.js.map +0 -1
- package/dist/__tests__/review-strategy.test.d.ts +0 -2
- package/dist/__tests__/review-strategy.test.d.ts.map +0 -1
- package/dist/__tests__/review-strategy.test.js +0 -195
- package/dist/__tests__/review-strategy.test.js.map +0 -1
- package/dist/__tests__/skill-attribution.test.d.ts +0 -2
- package/dist/__tests__/skill-attribution.test.d.ts.map +0 -1
- package/dist/__tests__/skill-attribution.test.js +0 -53
- package/dist/__tests__/skill-attribution.test.js.map +0 -1
- package/dist/__tests__/skill-broker.test.d.ts +0 -2
- package/dist/__tests__/skill-broker.test.d.ts.map +0 -1
- package/dist/__tests__/skill-broker.test.js +0 -144
- package/dist/__tests__/skill-broker.test.js.map +0 -1
- package/dist/__tests__/skill-catalog.test.d.ts +0 -2
- package/dist/__tests__/skill-catalog.test.d.ts.map +0 -1
- package/dist/__tests__/skill-catalog.test.js +0 -40
- package/dist/__tests__/skill-catalog.test.js.map +0 -1
- package/dist/__tests__/skill-installer.test.d.ts +0 -2
- package/dist/__tests__/skill-installer.test.d.ts.map +0 -1
- package/dist/__tests__/skill-installer.test.js +0 -96
- package/dist/__tests__/skill-installer.test.js.map +0 -1
- package/dist/__tests__/skills-config.test.d.ts +0 -2
- package/dist/__tests__/skills-config.test.d.ts.map +0 -1
- package/dist/__tests__/skills-config.test.js +0 -12
- package/dist/__tests__/skills-config.test.js.map +0 -1
- package/dist/commands/linear-test.d.ts +0 -18
- package/dist/commands/linear-test.d.ts.map +0 -1
- package/dist/commands/linear-test.js +0 -130
- package/dist/commands/linear-test.js.map +0 -1
- package/dist/commands/skill.d.ts +0 -2
- package/dist/commands/skill.d.ts.map +0 -1
- package/dist/commands/skill.js +0 -16
- package/dist/commands/skill.js.map +0 -1
- package/dist/config/review-strategy.json +0 -202
- package/dist/lib/repository-guidance.d.ts +0 -2
- package/dist/lib/repository-guidance.d.ts.map +0 -1
- package/dist/lib/repository-guidance.js +0 -55
- package/dist/lib/repository-guidance.js.map +0 -1
- package/dist/lib/review-strategy.d.ts +0 -62
- package/dist/lib/review-strategy.d.ts.map +0 -1
- package/dist/lib/review-strategy.js +0 -235
- package/dist/lib/review-strategy.js.map +0 -1
- package/dist/linear/client.d.ts +0 -18
- package/dist/linear/client.d.ts.map +0 -1
- package/dist/linear/client.js +0 -67
- package/dist/linear/client.js.map +0 -1
- package/dist/linear/comment.d.ts +0 -20
- package/dist/linear/comment.d.ts.map +0 -1
- package/dist/linear/comment.js +0 -57
- package/dist/linear/comment.js.map +0 -1
- package/dist/linear/identity.d.ts +0 -59
- package/dist/linear/identity.d.ts.map +0 -1
- package/dist/linear/identity.js +0 -187
- package/dist/linear/identity.js.map +0 -1
- package/dist/linear/notify.d.ts +0 -35
- package/dist/linear/notify.d.ts.map +0 -1
- package/dist/linear/notify.js +0 -76
- package/dist/linear/notify.js.map +0 -1
- package/dist/linear/ref.d.ts +0 -13
- package/dist/linear/ref.d.ts.map +0 -1
- package/dist/linear/ref.js +0 -90
- package/dist/linear/ref.js.map +0 -1
- package/dist/linear/verify.d.ts +0 -26
- package/dist/linear/verify.d.ts.map +0 -1
- package/dist/linear/verify.js +0 -67
- package/dist/linear/verify.js.map +0 -1
- package/dist/skills/attribution.d.ts +0 -4
- package/dist/skills/attribution.d.ts.map +0 -1
- package/dist/skills/attribution.js +0 -14
- package/dist/skills/attribution.js.map +0 -1
- package/dist/skills/broker-server.d.ts +0 -2
- package/dist/skills/broker-server.d.ts.map +0 -1
- package/dist/skills/broker-server.js +0 -17
- package/dist/skills/broker-server.js.map +0 -1
- package/dist/skills/broker.d.ts +0 -42
- package/dist/skills/broker.d.ts.map +0 -1
- package/dist/skills/broker.js +0 -285
- package/dist/skills/broker.js.map +0 -1
- package/dist/skills/catalog.d.ts +0 -28
- package/dist/skills/catalog.d.ts.map +0 -1
- package/dist/skills/catalog.js +0 -104
- package/dist/skills/catalog.js.map +0 -1
- package/dist/skills/installer.d.ts +0 -10
- package/dist/skills/installer.d.ts.map +0 -1
- package/dist/skills/installer.js +0 -138
- package/dist/skills/installer.js.map +0 -1
- package/dist/skills/integrity.d.ts +0 -4
- package/dist/skills/integrity.d.ts.map +0 -1
- package/dist/skills/integrity.js +0 -36
- package/dist/skills/integrity.js.map +0 -1
- package/docs/dynamic-thoroughness.md +0 -286
- package/docs/linear-identity-contract.md +0 -139
- package/docs/linear-identity.md +0 -293
package/dist/lib/runner.js
CHANGED
|
@@ -9,22 +9,15 @@ import { runFixStep, runCodexFixStep } from '../reviewers/fix.js';
|
|
|
9
9
|
import { runConflictResolveStep, findConflictedFiles } from '../reviewers/conflict-resolve.js';
|
|
10
10
|
import { parseVerdict, prependVerdictToComment, NULL_VERDICT_WARNING, applySeverityGate, SEVERITY_GATE_NOTE } from '../lib/verdict.js';
|
|
11
11
|
import { createGithubClient, postReviewComment, getLastCrossCheckCommentId, getLastCrossCheckReviewComment } from '../github/client.js';
|
|
12
|
-
import { resolveLinearAuth, withWorker } from '../linear/identity.js';
|
|
13
|
-
import { notifyLinear } from '../linear/notify.js';
|
|
14
|
-
import { shouldPostToLinear } from '../linear/comment.js';
|
|
15
|
-
import { getLinearCredentials } from '../config/loader.js';
|
|
16
12
|
import { acquireRemoteLock, releaseRemoteLock } from '../github/review-status.js';
|
|
17
13
|
import { log as fileLog, logError, classifyError } from '../lib/logger.js';
|
|
18
14
|
import { buildCommitTrailers } from '../lib/annotation.js';
|
|
19
15
|
import { resolveClaudeModel, resolveCodexModel } from '../lib/review-models.js';
|
|
20
16
|
import { buildStepIdentityFields } from '../lib/event-fields.js';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
17
|
+
import { buildFixAppliedCommentBody, buildConflictResolvedCommentBody, buildRetriedReviewBanner } from '../lib/comment-bodies.js';
|
|
18
|
+
import { loadWorkflow, loadHarnessSection, evaluateWhen } from '../lib/workflow.js';
|
|
23
19
|
import { isSubscriptionLimitError, isVendorUnavailableError } from '../lib/smart-switch.js';
|
|
24
20
|
import { tierTimeoutMs } from '../reviewers/tier-timeouts.js';
|
|
25
|
-
import { loadSkillCatalog } from '../skills/catalog.js';
|
|
26
|
-
import { createSkillActivationSession } from '../skills/broker.js';
|
|
27
|
-
import { formatSkillAttribution } from '../skills/attribution.js';
|
|
28
21
|
const MAX_CROSSCHECK_COMMITS = 5;
|
|
29
22
|
const FIX_RETRY_DELAY_MS = 2 * 60 * 1000;
|
|
30
23
|
const REVIEW_RETRY_DELAY_MS = 2 * 60 * 1000;
|
|
@@ -56,27 +49,33 @@ function isTransientApiError(err) {
|
|
|
56
49
|
export function getEffectiveStepType(stepType, isRecheckRun) {
|
|
57
50
|
return stepType === 'review' && isRecheckRun ? 'recheck' : stepType;
|
|
58
51
|
}
|
|
59
|
-
|
|
52
|
+
// Counts crosscheck-authored commits unique to this PR (ahead of base) rather
|
|
53
|
+
// than the branch's full history. Long-lived integration branches like
|
|
54
|
+
// `staging` accumulate [crosscheck] commits from many merged PRs — counting
|
|
55
|
+
// those would trip the per-PR fix-loop guard immediately and skip fix/recheck.
|
|
56
|
+
//
|
|
57
|
+
// Fails closed: when `origin/<base>` isn't available (e.g. clone fetched the
|
|
58
|
+
// base ref with `base_branch_fetch_skipped`), fall back to the full-history
|
|
59
|
+
// count rather than returning 0. Over-counting can stop fix early; returning 0
|
|
60
|
+
// would silently disable the cap and let runaway fix loops keep pushing.
|
|
61
|
+
export function countCrosscheckCommitsForPR(tmpDir, baseRef) {
|
|
60
62
|
const runLog = (args) => execFileSync('git', ['log', '--oneline', ...args], { cwd: tmpDir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
61
63
|
const count = (out) => out.split('\n').filter(l => l.includes('[crosscheck]')).length;
|
|
62
64
|
try {
|
|
63
|
-
return
|
|
65
|
+
return count(runLog([`origin/${baseRef}..HEAD`]));
|
|
64
66
|
}
|
|
65
67
|
catch {
|
|
66
68
|
// Scoped range unavailable — fall back to full history so the cap still
|
|
67
|
-
// applies.
|
|
68
|
-
// but that's preferable to bypassing the safety guard.
|
|
69
|
+
// applies. May over-count when the branch has prior merged crosscheck
|
|
70
|
+
// commits, but that's preferable to bypassing the safety guard.
|
|
69
71
|
try {
|
|
70
|
-
return
|
|
72
|
+
return count(runLog([]));
|
|
71
73
|
}
|
|
72
74
|
catch {
|
|
73
|
-
return
|
|
75
|
+
return 0;
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
|
-
export function countCrosscheckCommitsForPR(tmpDir, baseRef) {
|
|
78
|
-
return countCrosscheckCommitsForPRDetailed(tmpDir, baseRef).count;
|
|
79
|
-
}
|
|
80
79
|
export function buildWorkflowCompleteEvent(inputs) {
|
|
81
80
|
const stepValues = Object.values(inputs.results);
|
|
82
81
|
const lastVerdict = stepValues.reverse().find(r => r.verdict !== undefined)?.verdict ?? null;
|
|
@@ -357,47 +356,6 @@ export async function runWorkflow(ctx) {
|
|
|
357
356
|
// can iterate the same list and release these shas if SIGINT/SIGTERM fires
|
|
358
357
|
// mid-workflow (process.exit there bypasses our finally below).
|
|
359
358
|
const pushedShasNeedingRelease = ctx.pushedShas ?? [];
|
|
360
|
-
const skillCatalog = config.skills.enabled.length > 0 ? loadSkillCatalog() : [];
|
|
361
|
-
const skillSessions = new Map();
|
|
362
|
-
const skillSessionFor = (stepName, stepType) => {
|
|
363
|
-
if (skillCatalog.length === 0)
|
|
364
|
-
return undefined;
|
|
365
|
-
const existing = skillSessions.get(stepName);
|
|
366
|
-
if (existing)
|
|
367
|
-
return existing;
|
|
368
|
-
const session = createSkillActivationSession(stepType, config.skills.enabled, skillCatalog);
|
|
369
|
-
skillSessions.set(stepName, session);
|
|
370
|
-
return session;
|
|
371
|
-
};
|
|
372
|
-
// Skills were offered and the agent took none. Silent before, which is how a
|
|
373
|
-
// prompt that never triggered activation ran unnoticed for 336 steps.
|
|
374
|
-
// `skills_activated: []` on the *_complete events says something similar but
|
|
375
|
-
// quietly; what is new here is `enabled` (what was actually on offer) and the
|
|
376
|
-
// warn level, which makes a step that activates nothing greppable on its own.
|
|
377
|
-
const logSkillsNoneActivated = (session, identity) => {
|
|
378
|
-
// skillSessionFor hands back a session whenever the catalog is non-empty,
|
|
379
|
-
// but createSkillActivationSession filters that catalog down to
|
|
380
|
-
// config.skills.enabled — so a configured name that never resolved (typo,
|
|
381
|
-
// skill not installed) leaves enabledSkills empty, and then
|
|
382
|
-
// renderSkillBrokerInstructions renders nothing at all. Nothing was offered,
|
|
383
|
-
// so nothing was refused: that is broken config, not agent non-compliance,
|
|
384
|
-
// and logging it here would conflate the two causes the event exists to
|
|
385
|
-
// tell apart.
|
|
386
|
-
if (session.enabledSkills.length === 0)
|
|
387
|
-
return;
|
|
388
|
-
fileLog({ level: 'warn', event: 'skills_none_activated', repo: `${owner}/${repoName}`, pr: prNumber, ...identity, enabled: session.enabledSkills.map(skill => skill.name) });
|
|
389
|
-
};
|
|
390
|
-
// Linear write-back identity. Resolved up front — before any expensive step —
|
|
391
|
-
// and allowed to throw. The contract is that a configured-but-failing
|
|
392
|
-
// client_credentials mint ABORTS rather than degrading, because silently
|
|
393
|
-
// continuing would either drop the write or re-attribute it to a human. This
|
|
394
|
-
// matches commands/review.ts; the two paths must not disagree.
|
|
395
|
-
// The contract is one token per command run. runWorkflow is re-entered for every
|
|
396
|
-
// fix/recheck round under --crazy and max_rounds, so minting here would mint per
|
|
397
|
-
// round and let a late transient failure abort work already done. The caller
|
|
398
|
-
// resolves once and passes it in; resolving here is the single-round fallback.
|
|
399
|
-
// A dry run posts nothing, so it never mints.
|
|
400
|
-
let linearAuth = ctx.linearAuth ?? null;
|
|
401
359
|
let workflowFailed = false;
|
|
402
360
|
let workflowError = undefined;
|
|
403
361
|
let failedStep = undefined;
|
|
@@ -428,16 +386,6 @@ export async function runWorkflow(ctx) {
|
|
|
428
386
|
let currentStepName;
|
|
429
387
|
emitPRComplexity(ctx, triggerField);
|
|
430
388
|
try {
|
|
431
|
-
// Inside the try so a preflight failure still reaches the completion handler in
|
|
432
|
-
// the finally — resolving above it meant a failed mint skipped workflow_complete
|
|
433
|
-
// entirely and left no record of the run.
|
|
434
|
-
// canWriteVerdict here too: the caller passes null deliberately when the selected
|
|
435
|
-
// steps cannot write a verdict, and this fallback previously read that as
|
|
436
|
-
// "unresolved" and resolved anyway — defeating the gate one line upstream.
|
|
437
|
-
if (!ctx.dryRun && !linearAuth && linearWritePossible(config.linear, steps)) {
|
|
438
|
-
linearAuth = await resolveLinearAuth(config.linear, getLinearCredentials(config.linear.auth));
|
|
439
|
-
fileLog({ level: 'info', event: 'linear_auth_resolved', repo: `${owner}/${repoName}`, pr: prNumber, mode: linearAuth.mode, actor: linearAuth.actor });
|
|
440
|
-
}
|
|
441
389
|
for (const step of steps) {
|
|
442
390
|
currentStepName = step.name;
|
|
443
391
|
stepsRun.push(step.name);
|
|
@@ -500,19 +448,17 @@ export async function runWorkflow(ctx) {
|
|
|
500
448
|
let inputTokens;
|
|
501
449
|
let outputTokens;
|
|
502
450
|
let model = 'default';
|
|
503
|
-
let effort;
|
|
504
451
|
let retried;
|
|
505
|
-
const skillSession = skillSessionFor(step.name, effectiveType);
|
|
506
452
|
const runReviewWithVendor = async (candidate) => {
|
|
507
453
|
if (candidate === 'codex') {
|
|
508
454
|
;
|
|
509
|
-
({ review: rawReview, tokensUsed, model,
|
|
455
|
+
({ review: rawReview, tokensUsed, model, retried } = await runCodexReview(tmpDir, pr.base.ref, pr.title, config.quality, config.vendors.codex, step.instructions, undefined, ctx.overrideTimeoutMs ?? vendorTimeoutMs(config.vendors.codex.timeout_sec), log, ctx.issueContext));
|
|
510
456
|
inputTokens = undefined;
|
|
511
457
|
outputTokens = undefined;
|
|
512
458
|
}
|
|
513
459
|
else {
|
|
514
460
|
;
|
|
515
|
-
({ review: rawReview, tokensUsed, inputTokens, outputTokens, model,
|
|
461
|
+
({ review: rawReview, tokensUsed, inputTokens, outputTokens, model, retried } = await runClaudeReview(tmpDir, pr.base.ref, pr.title, config.quality, config.vendors.claude, config.budget.per_review_usd, step.instructions, undefined, ctx.overrideTimeoutMs ?? vendorTimeoutMs(config.vendors.claude.timeout_sec), !!ctx.roundMode, log, ctx.issueContext));
|
|
516
462
|
}
|
|
517
463
|
};
|
|
518
464
|
try {
|
|
@@ -569,11 +515,6 @@ export async function runWorkflow(ctx) {
|
|
|
569
515
|
}
|
|
570
516
|
}
|
|
571
517
|
}
|
|
572
|
-
const activatedSkills = skillSession?.activations() ?? [];
|
|
573
|
-
if (activatedSkills.length > 0)
|
|
574
|
-
log(chalk.dim(` skills: ${formatSkillAttribution(activatedSkills)}`));
|
|
575
|
-
else if (skillSession)
|
|
576
|
-
logSkillsNoneActivated(skillSession, stepIdentity);
|
|
577
518
|
// First attempt timed out but the delayed retry succeeded — surface a
|
|
578
519
|
// soft notice on the review comment so the author knows it was a transient blip.
|
|
579
520
|
if (retried) {
|
|
@@ -595,13 +536,11 @@ export async function runWorkflow(ctx) {
|
|
|
595
536
|
const baseBody = verdict === null
|
|
596
537
|
? `${NULL_VERDICT_WARNING}\n\n${clean}`
|
|
597
538
|
: prependVerdictToComment(gate.downgraded ? `${SEVERITY_GATE_NOTE}\n\n${clean}` : clean, verdict);
|
|
598
|
-
// Skills are not folded into the body — postReviewComment renders the
|
|
599
|
-
// receipt beneath the attribution footer.
|
|
600
539
|
const commentBody = retried
|
|
601
540
|
? `${buildRetriedReviewBanner(retried.timeoutMs, retried.delayMs)}\n\n${baseBody}`
|
|
602
541
|
: baseBody;
|
|
603
542
|
const commentCount = countComments(rawReview);
|
|
604
|
-
fileLog({ level: 'info', event: 'review_complete', repo: `${owner}/${repoName}`, pr: prNumber, reviewer, model, ...stepIdentity, verdict, duration_ms: Date.now() - stepStart, tokens_used: tokensUsed,
|
|
543
|
+
fileLog({ level: 'info', event: 'review_complete', repo: `${owner}/${repoName}`, pr: prNumber, reviewer, model, ...stepIdentity, verdict, duration_ms: Date.now() - stepStart, tokens_used: tokensUsed, ...(inputTokens !== undefined && { input_tokens: inputTokens }), ...(outputTokens !== undefined && { output_tokens: outputTokens }), ...(ctx.round !== undefined && { round: ctx.round }), ...(ctx.roundMode && { mode: ctx.roundMode }), ...triggerField });
|
|
605
544
|
// Recheck verdict is stored separately to preserve the original review's commentCount on the board
|
|
606
545
|
const phaseUpdate = isRecheck
|
|
607
546
|
? { recheckVerdict: verdict, phase: donePhase, recheckTokens: tokensUsed, recheckReviewer: reviewer, qualityTier: config.quality.tier }
|
|
@@ -640,41 +579,9 @@ export async function runWorkflow(ctx) {
|
|
|
640
579
|
annotationSha = execSync('git rev-parse HEAD', { cwd: tmpDir, encoding: 'utf8' }).trim();
|
|
641
580
|
}
|
|
642
581
|
catch { /* fall back to pr.head.sha if git is unavailable */ }
|
|
643
|
-
const commentId = await postReviewComment(octokit, owner, repoName, prNumber, commentBody, reviewer, config.brand, origin, verdict ?? undefined, priorReviewId, isRecheck, model, effectiveType, ctx.round ?? 1, annotationSha, nextStepAnnotation, ctx.trigger === 'kickass' ? 'kickass' : undefined
|
|
582
|
+
const commentId = await postReviewComment(octokit, owner, repoName, prNumber, commentBody, reviewer, config.brand, origin, verdict ?? undefined, priorReviewId, isRecheck, model, effectiveType, ctx.round ?? 1, annotationSha, nextStepAnnotation, ctx.trigger === 'kickass' ? 'kickass' : undefined);
|
|
644
583
|
const commentUrl = `github.com/${owner}/${repoName}/pull/${prNumber}`;
|
|
645
584
|
fileLog({ level: 'info', event: 'comment_posted', repo: `${owner}/${repoName}`, pr: prNumber, url: `https://${commentUrl}` });
|
|
646
|
-
// Mirror the verdict onto the PR's Linear issue. `run` and `watch` both
|
|
647
|
-
// land here, so this is the path that matters — reviews posted from
|
|
648
|
-
// commands/review.ts are the exception, not the rule.
|
|
649
|
-
if (linearAuth && shouldPostToLinear(verdict ?? null, config.linear.comment_on)) {
|
|
650
|
-
{
|
|
651
|
-
// Attribute to crosscheck/review, /fix, /recheck rather than a flat actor.
|
|
652
|
-
const stepAuth = config.linear.identity.per_step_actor ? withWorker(linearAuth, effectiveType) : linearAuth;
|
|
653
|
-
const linearResult = await notifyLinear({
|
|
654
|
-
auth: stepAuth,
|
|
655
|
-
config: config.linear,
|
|
656
|
-
pr: { branch: pr.head.ref, title: pr.title, body: pr.body ?? '', url: `https://${commentUrl}`, sha: annotationSha },
|
|
657
|
-
verdict: verdict ?? null,
|
|
658
|
-
reviewer,
|
|
659
|
-
origin,
|
|
660
|
-
model,
|
|
661
|
-
stepType: effectiveType,
|
|
662
|
-
round: ctx.round ?? 1,
|
|
663
|
-
service: config.brand.service_name,
|
|
664
|
-
});
|
|
665
|
-
fileLog({
|
|
666
|
-
level: linearResult.status === 'failed' ? 'warn' : 'info',
|
|
667
|
-
event: 'linear_comment', repo: `${owner}/${repoName}`, pr: prNumber,
|
|
668
|
-
status: linearResult.status, reason: linearResult.reason, issue: linearResult.identifier,
|
|
669
|
-
});
|
|
670
|
-
if (linearResult.status === 'posted') {
|
|
671
|
-
log(chalk.dim(` linear: commented on ${linearResult.identifier}`));
|
|
672
|
-
}
|
|
673
|
-
else if (linearResult.status === 'failed') {
|
|
674
|
-
log(chalk.yellow(` linear: write failed — ${linearResult.reason}`));
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
585
|
results[step.name] = { verdict, commentBody, commentUrl, commentId, tokens_used: tokensUsed, input_tokens: inputTokens, output_tokens: outputTokens, vendor: reviewer, model };
|
|
679
586
|
}
|
|
680
587
|
}
|
|
@@ -740,16 +647,11 @@ export async function runWorkflow(ctx) {
|
|
|
740
647
|
// Scope to commits ahead of base so long-lived branches (e.g. staging)
|
|
741
648
|
// don't count [crosscheck] commits from previously merged PRs.
|
|
742
649
|
// Crazy/halfcrazy mode doubles the cap since it deliberately loops.
|
|
743
|
-
const
|
|
650
|
+
const existingCount = countCrosscheckCommitsForPR(tmpDir, pr.base.ref);
|
|
744
651
|
const effectiveCommitLimit = ctx.roundMode ? MAX_CROSSCHECK_COMMITS * 2 : MAX_CROSSCHECK_COMMITS;
|
|
745
|
-
if (
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
// than a real cap hit — say so, or the next person debugs the wrong thing.
|
|
749
|
-
log(commitCount.scoped
|
|
750
|
-
? chalk.yellow(`⚠ PR #${prNumber}: ${commitCount.count}/${effectiveCommitLimit} [crosscheck] commits already — stopping auto-fix`)
|
|
751
|
-
: chalk.yellow(`⚠ PR #${prNumber}: cannot scope [crosscheck] commit count (origin/${pr.base.ref} missing; ${commitCount.count} across all history) — stopping auto-fix`));
|
|
752
|
-
skipFix(commitCount.scoped ? 'commit_limit_reached' : 'commit_count_unscoped');
|
|
652
|
+
if (existingCount >= effectiveCommitLimit) {
|
|
653
|
+
log(chalk.yellow(`⚠ PR #${prNumber}: ${effectiveCommitLimit} [crosscheck] commits already — stopping auto-fix`));
|
|
654
|
+
skipFix('commit_limit_reached');
|
|
753
655
|
continue;
|
|
754
656
|
}
|
|
755
657
|
onPhaseChange(`${vendor} fixing...`, { phase: 'fixing' });
|
|
@@ -757,20 +659,18 @@ export async function runWorkflow(ctx) {
|
|
|
757
659
|
let appliedCount = 0;
|
|
758
660
|
let fixChangedFiles = [];
|
|
759
661
|
let fixTokensUsed;
|
|
760
|
-
let fixEffort;
|
|
761
662
|
let fixErr = undefined;
|
|
762
663
|
let activeVendor = vendor;
|
|
763
664
|
const tierMs = tierTimeoutMs(config.quality.tier);
|
|
764
|
-
const skillSession = skillSessionFor(step.name, effectiveType);
|
|
765
665
|
const runFix = async (v) => {
|
|
766
666
|
if (v === 'codex') {
|
|
767
|
-
return runCodexFixStep(tmpDir, pr.base.ref, pr.title, reviewCommentBody, step.instructions ?? '', codexFixModel, ctx.overrideTimeoutMs ?? vendorTimeoutMs(config.vendors.codex.timeout_sec) ?? tierMs
|
|
667
|
+
return runCodexFixStep(tmpDir, pr.base.ref, pr.title, reviewCommentBody, step.instructions ?? '', codexFixModel, ctx.overrideTimeoutMs ?? vendorTimeoutMs(config.vendors.codex.timeout_sec) ?? tierMs);
|
|
768
668
|
}
|
|
769
|
-
return runFixStep(tmpDir, pr.base.ref, pr.title, reviewCommentBody, step.instructions ?? '', config, claudeFixModel, ctx.overrideTimeoutMs ?? vendorTimeoutMs(config.vendors.claude.timeout_sec) ?? tierMs
|
|
669
|
+
return runFixStep(tmpDir, pr.base.ref, pr.title, reviewCommentBody, step.instructions ?? '', config, claudeFixModel, ctx.overrideTimeoutMs ?? vendorTimeoutMs(config.vendors.claude.timeout_sec) ?? tierMs);
|
|
770
670
|
};
|
|
771
671
|
try {
|
|
772
672
|
;
|
|
773
|
-
({ appliedCount, changedFiles: fixChangedFiles, tokensUsed: fixTokensUsed
|
|
673
|
+
({ appliedCount, changedFiles: fixChangedFiles, tokensUsed: fixTokensUsed } = await runFix(vendor));
|
|
774
674
|
}
|
|
775
675
|
catch (err) {
|
|
776
676
|
logError({ repo: `${owner}/${repoName}`, pr: prNumber, phase: 'fix', attempt: 1, vendor }, err);
|
|
@@ -784,7 +684,7 @@ export async function runWorkflow(ctx) {
|
|
|
784
684
|
fileLog({ level: 'warn', event: 'fix_vendor_fallback', repo: `${owner}/${repoName}`, pr: prNumber, from: vendor, to: fallbackVendor, ...(isSubscriptionLimitError(err) && { reason: 'vendor_limit' }) });
|
|
785
685
|
try {
|
|
786
686
|
;
|
|
787
|
-
({ appliedCount, changedFiles: fixChangedFiles, tokensUsed: fixTokensUsed
|
|
687
|
+
({ appliedCount, changedFiles: fixChangedFiles, tokensUsed: fixTokensUsed } = await runFix(fallbackVendor));
|
|
788
688
|
activeVendor = fallbackVendor;
|
|
789
689
|
}
|
|
790
690
|
catch (fallbackErr) {
|
|
@@ -805,7 +705,7 @@ export async function runWorkflow(ctx) {
|
|
|
805
705
|
onPhaseChange(`${activeVendor} fixing (retry)...`, { phase: 'fixing' });
|
|
806
706
|
try {
|
|
807
707
|
;
|
|
808
|
-
({ appliedCount, changedFiles: fixChangedFiles, tokensUsed: fixTokensUsed
|
|
708
|
+
({ appliedCount, changedFiles: fixChangedFiles, tokensUsed: fixTokensUsed } = await runFix(activeVendor));
|
|
809
709
|
fileLog({ level: 'info', event: 'fix_retry_succeeded', repo: `${owner}/${repoName}`, pr: prNumber });
|
|
810
710
|
fixErr = undefined;
|
|
811
711
|
}
|
|
@@ -814,20 +714,6 @@ export async function runWorkflow(ctx) {
|
|
|
814
714
|
fixErr = retryErr;
|
|
815
715
|
}
|
|
816
716
|
}
|
|
817
|
-
const activatedSkills = skillSession?.activations() ?? [];
|
|
818
|
-
if (activatedSkills.length > 0)
|
|
819
|
-
log(chalk.dim(` skills: ${formatSkillAttribution(activatedSkills)}`));
|
|
820
|
-
else if (skillSession)
|
|
821
|
-
logSkillsNoneActivated(skillSession, { step_type: 'fix', step_name: step.name });
|
|
822
|
-
// Every delivery mode (commit card, fix PR, suggested-diff comment) closes
|
|
823
|
-
// with the same footer as a review comment.
|
|
824
|
-
const fixAttributionFooter = () => buildAttributionFooter({
|
|
825
|
-
action: 'Fixed',
|
|
826
|
-
vendor: activeVendor,
|
|
827
|
-
model: activeVendor === 'codex' ? codexFixModel : claudeFixModel,
|
|
828
|
-
effort: fixEffort,
|
|
829
|
-
skills: activatedSkills,
|
|
830
|
-
});
|
|
831
717
|
if (fixErr !== undefined) {
|
|
832
718
|
skipFix(isSubscriptionLimitError(fixErr) ? 'vendor_limit' : 'fix_error');
|
|
833
719
|
// Only notify for transient failures — auth errors are operator issues, not PR author issues
|
|
@@ -836,13 +722,7 @@ export async function runWorkflow(ctx) {
|
|
|
836
722
|
const octokit = createGithubClient(token);
|
|
837
723
|
await octokit.rest.issues.createComment({
|
|
838
724
|
owner, repo: repoName, issue_number: prNumber,
|
|
839
|
-
body:
|
|
840
|
-
prUrl: pr.html_url,
|
|
841
|
-
vendor: activeVendor,
|
|
842
|
-
model: activeVendor === 'codex' ? codexFixModel : claudeFixModel,
|
|
843
|
-
effort: fixEffort,
|
|
844
|
-
skills: activatedSkills,
|
|
845
|
-
}),
|
|
725
|
+
body: `⚠️ **Auto-fix failed**\n\nThe fix step timed out after retrying. Push a new commit or run \`crosscheck run ${pr.html_url}\` to retry manually.\n\n<!-- crosscheck: fix_failed -->`,
|
|
846
726
|
});
|
|
847
727
|
fileLog({ level: 'info', event: 'fix_failed_comment_posted', repo: `${owner}/${repoName}`, pr: prNumber });
|
|
848
728
|
}
|
|
@@ -861,7 +741,7 @@ export async function runWorkflow(ctx) {
|
|
|
861
741
|
// event — NOT `fix_complete` — so status consumers (WORKFLOW_ACTIVITY_EVENTS,
|
|
862
742
|
// the legacy status fold) don't misread a no-op as real workflow progress and
|
|
863
743
|
// mark the PR NEEDS_RECHECK or hide it from `scan --tidy` with no fix applied.
|
|
864
|
-
fileLog({ level: 'info', event: 'fix_noop', repo: `${owner}/${repoName}`, pr: prNumber, vendor: activeVendor, applied_count: 0, no_changes: true, tokens_used: fixTokensUsed,
|
|
744
|
+
fileLog({ level: 'info', event: 'fix_noop', repo: `${owner}/${repoName}`, pr: prNumber, vendor: activeVendor, applied_count: 0, no_changes: true, tokens_used: fixTokensUsed, duration_ms: Date.now() - fixStepStart, ...triggerField });
|
|
865
745
|
results[step.name] = { applied_count: 0, ...(fixTokensUsed !== undefined && { tokens_used: fixTokensUsed }), vendor };
|
|
866
746
|
continue;
|
|
867
747
|
}
|
|
@@ -913,7 +793,7 @@ export async function runWorkflow(ctx) {
|
|
|
913
793
|
}
|
|
914
794
|
}
|
|
915
795
|
onPhaseChange('fixed ✓', { fixCount: appliedCount, phase: 'fixed', fixTokens: fixTokensUsed });
|
|
916
|
-
fileLog({ level: 'info', event: 'fix_complete', repo: `${owner}/${repoName}`, pr: prNumber, vendor: activeVendor, applied_count: appliedCount, sha: newSha, delivery: 'commit', tokens_used: fixTokensUsed,
|
|
796
|
+
fileLog({ level: 'info', event: 'fix_complete', repo: `${owner}/${repoName}`, pr: prNumber, vendor: activeVendor, applied_count: appliedCount, sha: newSha, delivery: 'commit', tokens_used: fixTokensUsed, duration_ms: Date.now() - fixStepStart, ...triggerField });
|
|
917
797
|
// Post a summary comment so the silent commit push is visible on the timeline
|
|
918
798
|
// as a comment card. Best-effort — a failure here must not fail the run.
|
|
919
799
|
try {
|
|
@@ -924,9 +804,6 @@ export async function runWorkflow(ctx) {
|
|
|
924
804
|
changedFiles: fixChangedFiles,
|
|
925
805
|
vendor: activeVendor,
|
|
926
806
|
reviewCommentBody,
|
|
927
|
-
model: activeVendor === 'codex' ? codexFixModel : claudeFixModel,
|
|
928
|
-
effort: fixEffort,
|
|
929
|
-
skills: activatedSkills,
|
|
930
807
|
});
|
|
931
808
|
await octokit.rest.issues.createComment({ owner, repo: repoName, issue_number: prNumber, body });
|
|
932
809
|
fileLog({ level: 'info', event: 'fix_applied_comment_posted', repo: `${owner}/${repoName}`, pr: prNumber, sha: newSha });
|
|
@@ -963,13 +840,7 @@ export async function runWorkflow(ctx) {
|
|
|
963
840
|
head: fixBranch,
|
|
964
841
|
base: pr.head.ref,
|
|
965
842
|
title: fixPrTitle,
|
|
966
|
-
body:
|
|
967
|
-
`Auto-fix by crosscheck for CR issues found in #${prNumber}.`,
|
|
968
|
-
'',
|
|
969
|
-
`Review: https://github.com/${owner}/${repoName}/pull/${prNumber}`,
|
|
970
|
-
'',
|
|
971
|
-
fixAttributionFooter(),
|
|
972
|
-
].join('\n'),
|
|
843
|
+
body: `Auto-fix by crosscheck for CR issues found in #${prNumber}.\n\nReview: https://github.com/${owner}/${repoName}/pull/${prNumber}`,
|
|
973
844
|
});
|
|
974
845
|
if (config.post_review.auto_fix.delivery.label) {
|
|
975
846
|
try {
|
|
@@ -980,7 +851,7 @@ export async function runWorkflow(ctx) {
|
|
|
980
851
|
catch { /* label may not exist in this repo — skip */ }
|
|
981
852
|
}
|
|
982
853
|
onPhaseChange('fixed ✓', { fixCount: appliedCount, phase: 'fixed', fixTokens: fixTokensUsed });
|
|
983
|
-
fileLog({ level: 'info', event: 'fix_complete', repo: `${owner}/${repoName}`, pr: prNumber, vendor: activeVendor, applied_count: appliedCount, sha: newSha, delivery: 'pull_request', fix_pr: fixPr.number, tokens_used: fixTokensUsed,
|
|
854
|
+
fileLog({ level: 'info', event: 'fix_complete', repo: `${owner}/${repoName}`, pr: prNumber, vendor: activeVendor, applied_count: appliedCount, sha: newSha, delivery: 'pull_request', fix_pr: fixPr.number, tokens_used: fixTokensUsed, duration_ms: Date.now() - fixStepStart, ...triggerField });
|
|
984
855
|
results[step.name] = { applied_count: appliedCount, tokens_used: fixTokensUsed, vendor: activeVendor };
|
|
985
856
|
}
|
|
986
857
|
else {
|
|
@@ -992,11 +863,11 @@ export async function runWorkflow(ctx) {
|
|
|
992
863
|
catch { /* ignore */ }
|
|
993
864
|
if (patch) {
|
|
994
865
|
const octokit = createGithubClient(token);
|
|
995
|
-
const body = `### Suggested fixes (crosscheck auto-fix)\n\n\`\`\`diff\n${patch.slice(0, 16000)}\n
|
|
866
|
+
const body = `### Suggested fixes (crosscheck auto-fix)\n\n\`\`\`diff\n${patch.slice(0, 16000)}\n\`\`\``;
|
|
996
867
|
await octokit.rest.issues.createComment({ owner, repo: repoName, issue_number: prNumber, body });
|
|
997
868
|
}
|
|
998
869
|
onPhaseChange('fixed ✓', { fixCount: appliedCount, phase: 'fixed', fixTokens: fixTokensUsed });
|
|
999
|
-
fileLog({ level: 'info', event: 'fix_complete', repo: `${owner}/${repoName}`, pr: prNumber, vendor: activeVendor, applied_count: appliedCount, delivery: 'comment', tokens_used: fixTokensUsed,
|
|
870
|
+
fileLog({ level: 'info', event: 'fix_complete', repo: `${owner}/${repoName}`, pr: prNumber, vendor: activeVendor, applied_count: appliedCount, delivery: 'comment', tokens_used: fixTokensUsed, duration_ms: Date.now() - fixStepStart, ...triggerField });
|
|
1000
871
|
results[step.name] = { applied_count: appliedCount, tokens_used: fixTokensUsed, vendor: activeVendor };
|
|
1001
872
|
}
|
|
1002
873
|
}
|
|
@@ -1080,15 +951,13 @@ export async function runWorkflow(ctx) {
|
|
|
1080
951
|
skipConflictResolve('fork_pr');
|
|
1081
952
|
continue;
|
|
1082
953
|
}
|
|
1083
|
-
const
|
|
1084
|
-
if (
|
|
954
|
+
const existingCount = countCrosscheckCommitsForPR(tmpDir, pr.base.ref);
|
|
955
|
+
if (existingCount >= MAX_CROSSCHECK_COMMITS) {
|
|
1085
956
|
try {
|
|
1086
957
|
execSync('git merge --abort', { cwd: tmpDir });
|
|
1087
958
|
}
|
|
1088
959
|
catch { /* ignore */ }
|
|
1089
|
-
log(
|
|
1090
|
-
? chalk.yellow(`⚠ PR #${prNumber}: ${crCommitCount.count}/${MAX_CROSSCHECK_COMMITS} [crosscheck] commits already — stopping conflict-resolve`)
|
|
1091
|
-
: chalk.yellow(`⚠ PR #${prNumber}: cannot scope [crosscheck] commit count (origin/${pr.base.ref} missing) — stopping conflict-resolve`));
|
|
960
|
+
log(chalk.yellow(`⚠ PR #${prNumber}: ${MAX_CROSSCHECK_COMMITS} [crosscheck] commits already — stopping conflict-resolve`));
|
|
1092
961
|
skipConflictResolve('commit_limit_reached');
|
|
1093
962
|
continue;
|
|
1094
963
|
}
|
|
@@ -1098,11 +967,9 @@ export async function runWorkflow(ctx) {
|
|
|
1098
967
|
let appliedCount = 0;
|
|
1099
968
|
let resolvedPaths = [];
|
|
1100
969
|
let resolveTokensUsed;
|
|
1101
|
-
let resolveEffort;
|
|
1102
|
-
const skillSession = skillSessionFor(step.name, effectiveType);
|
|
1103
970
|
try {
|
|
1104
971
|
;
|
|
1105
|
-
({ appliedCount, resolvedPaths, tokensUsed: resolveTokensUsed
|
|
972
|
+
({ appliedCount, resolvedPaths, tokensUsed: resolveTokensUsed } = await runConflictResolveStep(tmpDir, pr.title, step.instructions ?? '', conflictResolveModel, ctx.overrideTimeoutMs ?? vendorTimeoutMs(config.vendors.claude.timeout_sec)));
|
|
1106
973
|
}
|
|
1107
974
|
catch (err) {
|
|
1108
975
|
logError({ repo: `${owner}/${repoName}`, pr: prNumber, phase: 'conflict-resolve', attempt: 1 }, err);
|
|
@@ -1113,11 +980,6 @@ export async function runWorkflow(ctx) {
|
|
|
1113
980
|
skipConflictResolve(isSubscriptionLimitError(err) ? 'vendor_limit' : 'resolve_error');
|
|
1114
981
|
continue;
|
|
1115
982
|
}
|
|
1116
|
-
const activatedSkills = skillSession?.activations() ?? [];
|
|
1117
|
-
if (activatedSkills.length > 0)
|
|
1118
|
-
log(chalk.dim(` skills: ${formatSkillAttribution(activatedSkills)}`));
|
|
1119
|
-
else if (skillSession)
|
|
1120
|
-
logSkillsNoneActivated(skillSession, { step_type: 'conflict-resolve', step_name: step.name });
|
|
1121
983
|
if (appliedCount === 0) {
|
|
1122
984
|
try {
|
|
1123
985
|
execSync('git merge --abort', { cwd: tmpDir });
|
|
@@ -1220,7 +1082,7 @@ export async function runWorkflow(ctx) {
|
|
|
1220
1082
|
fileLog({ level: 'warn', event: 'remote_lock_refresh_failed', repo: `${owner}/${repoName}`, pr: prNumber, sha: newSha, error: err instanceof Error ? err.message : String(err) });
|
|
1221
1083
|
}
|
|
1222
1084
|
onPhaseChange('conflicts resolved ✓', { fixCount: appliedCount, phase: 'fixed', fixTokens: resolveTokensUsed });
|
|
1223
|
-
fileLog({ level: 'info', event: 'conflict_resolve_complete', repo: `${owner}/${repoName}`, pr: prNumber, vendor, conflicts_resolved: conflictedFiles.length, sha: newSha, tokens_used: resolveTokensUsed,
|
|
1085
|
+
fileLog({ level: 'info', event: 'conflict_resolve_complete', repo: `${owner}/${repoName}`, pr: prNumber, vendor, conflicts_resolved: conflictedFiles.length, sha: newSha, tokens_used: resolveTokensUsed, duration_ms: Date.now() - conflictResolveStepStart, ...triggerField });
|
|
1224
1086
|
// Post a summary comment so the silent merge-commit push is visible on the
|
|
1225
1087
|
// timeline as a comment card. Best-effort — a failure here must not fail the run.
|
|
1226
1088
|
// Prefer the resolver's actual rewrite set; fall back to the originally-conflicted
|
|
@@ -1231,9 +1093,6 @@ export async function runWorkflow(ctx) {
|
|
|
1231
1093
|
owner, repo: repoName, sha: newSha,
|
|
1232
1094
|
conflictCount: conflictedFiles.length,
|
|
1233
1095
|
files: resolvedPaths.length > 0 ? resolvedPaths : conflictedFiles,
|
|
1234
|
-
model: conflictResolveModel,
|
|
1235
|
-
effort: resolveEffort,
|
|
1236
|
-
skills: activatedSkills,
|
|
1237
1096
|
});
|
|
1238
1097
|
await octokit.rest.issues.createComment({ owner, repo: repoName, issue_number: prNumber, body });
|
|
1239
1098
|
fileLog({ level: 'info', event: 'conflict_resolved_comment_posted', repo: `${owner}/${repoName}`, pr: prNumber, sha: newSha });
|
|
@@ -1270,9 +1129,6 @@ export async function runWorkflow(ctx) {
|
|
|
1270
1129
|
throw err;
|
|
1271
1130
|
}
|
|
1272
1131
|
finally {
|
|
1273
|
-
for (const session of skillSessions.values())
|
|
1274
|
-
session.close();
|
|
1275
|
-
skillSessions.clear();
|
|
1276
1132
|
if (pushedShasNeedingRelease.length > 0 || fixPushedShaRequiresRecheck !== null) {
|
|
1277
1133
|
const lockOctokit = createGithubClient(token);
|
|
1278
1134
|
const outcome = workflowFailed ? 'failure' : 'success';
|