codex-orchestrator 2.0.19 → 2.0.21
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/CHANGELOG.md +27 -0
- package/README.md +26 -1
- package/dist/src/v2/cli.d.ts +1 -0
- package/dist/src/v2/cli.d.ts.map +1 -1
- package/dist/src/v2/cli.js +9 -4
- package/dist/src/v2/cli.js.map +1 -1
- package/dist/src/v2/code-review-report.d.ts +3 -0
- package/dist/src/v2/code-review-report.d.ts.map +1 -1
- package/dist/src/v2/code-review-report.js +30 -1
- package/dist/src/v2/code-review-report.js.map +1 -1
- package/dist/src/v2/contained-report-operation.d.ts +1 -1
- package/dist/src/v2/contained-report-operation.d.ts.map +1 -1
- package/dist/src/v2/contained-report-operation.js +21 -5
- package/dist/src/v2/contained-report-operation.js.map +1 -1
- package/dist/src/v2/containment.d.ts.map +1 -1
- package/dist/src/v2/containment.js +12 -1
- package/dist/src/v2/containment.js.map +1 -1
- package/dist/src/v2/direct-delivery.d.ts +6 -1
- package/dist/src/v2/direct-delivery.d.ts.map +1 -1
- package/dist/src/v2/direct-delivery.js +30 -2
- package/dist/src/v2/direct-delivery.js.map +1 -1
- package/dist/src/v2/implementation-reviewer.d.ts +4 -2
- package/dist/src/v2/implementation-reviewer.d.ts.map +1 -1
- package/dist/src/v2/implementation-reviewer.js +36 -3
- package/dist/src/v2/implementation-reviewer.js.map +1 -1
- package/dist/src/v2/run-issue.d.ts +3 -0
- package/dist/src/v2/run-issue.d.ts.map +1 -1
- package/dist/src/v2/run-issue.js +149 -30
- package/dist/src/v2/run-issue.js.map +1 -1
- package/dist/src/v2/run-store.d.ts +1 -0
- package/dist/src/v2/run-store.d.ts.map +1 -1
- package/dist/src/v2/run-store.js +24 -3
- package/dist/src/v2/run-store.js.map +1 -1
- package/dist/src/v2/runtime.js +1 -1
- package/dist/src/v2/runtime.js.map +1 -1
- package/dist/src/v2/validation-progression.d.ts +4 -1
- package/dist/src/v2/validation-progression.d.ts.map +1 -1
- package/dist/src/v2/validation-progression.js +7 -1
- package/dist/src/v2/validation-progression.js.map +1 -1
- package/internal-workflow/manifest.json +1 -1
- package/internal-workflow/operations/code-review/SKILL.md +15 -1
- package/internal-workflow/profiles/review_coordinator.toml +1 -1
- package/internal-workflow/schemas/code-review-v1.json +1 -1
- package/package.json +1 -1
package/dist/src/v2/run-issue.js
CHANGED
|
@@ -5,6 +5,7 @@ import { canonicalJson, containsCredentialEvidence, containsHostIdentityEvidence
|
|
|
5
5
|
import { findDeniedPathMatch } from './adapters/path-policy.js';
|
|
6
6
|
import { adoptActiveAttempt, confirmActiveAttemptCleanup, createActiveAttempt, launchActiveAttempt, observeActiveAttempt, } from './active-attempt.js';
|
|
7
7
|
import { validateImplementationReport } from './implementation-report.js';
|
|
8
|
+
import { COMPLETE_REVIEW_COVERAGE, validateReviewContext } from './code-review-report.js';
|
|
8
9
|
import { validateCompletedReport } from './contained-report-operation.js';
|
|
9
10
|
import { directReviewCandidateTargetFingerprint, projectTerminalDirectReview, } from './direct-delivery.js';
|
|
10
11
|
import { MAX_REVIEW_PATCH_BYTES } from './implementation-reviewer.js';
|
|
@@ -17,6 +18,11 @@ import { createIssueDeliveryAuthority } from './delivery-authority.js';
|
|
|
17
18
|
import { claimComment, claimRunId, freezeCriteria, historicalClaimCounts, isClaimMarkerComment, isExactClaimMarkerLine, isAdoptableAttempt, observedHistoricalClaimKeys, outcomeEvidenceBytes, publicOutcome, publicIssueSnapshot, refreshClaimedIssueSnapshot, snapshotIssue, sameInspectionIdentity, sameStrings, semanticChangesMatch, } from './run-state-projections.js';
|
|
18
19
|
import { applyValidationTransition, nextValidationTransition, projectValidationFeedbackActivation, projectValidationProofPassed, projectValidationProofStart, projectValidationRepair, projectValidationReviewApproved, projectValidationReviewNeedsWork, projectValidationReviewReportRepair, projectValidationReviewTransportRetry, } from './validation-progression.js';
|
|
19
20
|
import { settleCommentEffect, settleCommitEffect, settleCleanupEffect, settleDraftPullRequestEffect, settleLabelsEffect, settlePushEffect, } from './pending-effect-settlement.js';
|
|
21
|
+
class ReviewTargetCredentialError extends Error {
|
|
22
|
+
constructor() {
|
|
23
|
+
super('Review target contains potential credential material; remove real credentials or use explicit dummy session tokens.');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
20
26
|
export class OwnerLockSafetyError extends Error {
|
|
21
27
|
}
|
|
22
28
|
export class OwnerLockContentionError extends Error {
|
|
@@ -925,12 +931,14 @@ export class RunIssue {
|
|
|
925
931
|
let active;
|
|
926
932
|
try {
|
|
927
933
|
assertPositiveInteger(input.issueNumber, 'issueNumber');
|
|
934
|
+
if (input.retryProofRunId !== undefined)
|
|
935
|
+
assertUuid(input.retryProofRunId);
|
|
928
936
|
const targetRoot = resolve(input.targetRoot);
|
|
929
937
|
const initialConfig = await this.readStrictConfig(targetRoot);
|
|
930
938
|
const canonicalRepository = `${initialConfig.config.github.owner.toLowerCase()}/${initialConfig.config.github.repo.toLowerCase()}`;
|
|
931
939
|
const preflightState = await this.dependencies.runRecords.inspect();
|
|
932
940
|
if (preflightState.status === 'unsupported')
|
|
933
|
-
return { status: 'state-schema-unsupported' };
|
|
941
|
+
return { status: 'state-schema-unsupported', ...(preflightState.reason ? { reason: preflightState.reason } : {}) };
|
|
934
942
|
try {
|
|
935
943
|
owner = await this.dependencies.ownerLock.acquire({ canonicalRepository, targetRoot });
|
|
936
944
|
}
|
|
@@ -961,11 +969,11 @@ export class RunIssue {
|
|
|
961
969
|
return await this.preClaimCancelled(input.issueNumber);
|
|
962
970
|
let authoritativeState = await this.dependencies.runRecords.inspect();
|
|
963
971
|
if (authoritativeState.status === 'unsupported')
|
|
964
|
-
return { status: 'state-schema-unsupported' };
|
|
972
|
+
return { status: 'state-schema-unsupported', ...(authoritativeState.reason ? { reason: authoritativeState.reason } : {}) };
|
|
965
973
|
if (!sameInspectionIdentity(preflightState, authoritativeState)) {
|
|
966
974
|
const stableState = await this.dependencies.runRecords.inspect();
|
|
967
975
|
if (stableState.status === 'unsupported')
|
|
968
|
-
return { status: 'state-schema-unsupported' };
|
|
976
|
+
return { status: 'state-schema-unsupported', ...(stableState.reason ? { reason: stableState.reason } : {}) };
|
|
969
977
|
if (!sameInspectionIdentity(authoritativeState, stableState)) {
|
|
970
978
|
return { status: 'requeued', reason: 'state-changed' };
|
|
971
979
|
}
|
|
@@ -1003,6 +1011,13 @@ export class RunIssue {
|
|
|
1003
1011
|
if (matchingRuns.length > 1)
|
|
1004
1012
|
return await this.preClaimInternal('ambiguous-run-state', input.issueNumber);
|
|
1005
1013
|
const existing = matchingRuns[0];
|
|
1014
|
+
if (input.retryProofRunId !== undefined && (existing?.runId !== input.retryProofRunId
|
|
1015
|
+
|| existing.terminalOutcome?.status !== 'internal-error'
|
|
1016
|
+
|| existing.terminalOutcome.code !== 'acceptance-proof-internal-failure')) {
|
|
1017
|
+
const reason = 'Proof retry requires the exact run with a terminal Acceptance Proof internal failure.';
|
|
1018
|
+
const evidence = await this.dependencies.writeEvidence({ runId: `issue-${input.issueNumber}`, code: 'proof-retry-not-eligible', summary: reason });
|
|
1019
|
+
return { status: 'not-eligible', reason, evidencePath: evidence.path };
|
|
1020
|
+
}
|
|
1006
1021
|
let issueSnapshot;
|
|
1007
1022
|
let frozenCriteria;
|
|
1008
1023
|
let runId;
|
|
@@ -1065,6 +1080,23 @@ export class RunIssue {
|
|
|
1065
1080
|
return reconciled;
|
|
1066
1081
|
active = reconciled.active;
|
|
1067
1082
|
}
|
|
1083
|
+
if (input.retryProofRunId !== undefined) {
|
|
1084
|
+
try {
|
|
1085
|
+
const recovered = await this.retryTerminalProof(active, config);
|
|
1086
|
+
if ('status' in recovered)
|
|
1087
|
+
return recovered;
|
|
1088
|
+
active = recovered.active;
|
|
1089
|
+
}
|
|
1090
|
+
catch {
|
|
1091
|
+
// Recovery uncertainty must not replace the terminal outcome that authorizes this retry.
|
|
1092
|
+
try {
|
|
1093
|
+
return await this.invokedFailure(active, 'acceptance-proof-retry-validation-retryable', 'Proof retry validation or its state transition could not be confirmed; inspect durable state before retrying.');
|
|
1094
|
+
}
|
|
1095
|
+
catch {
|
|
1096
|
+
return publicOutcome(active.record.terminalOutcome);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1068
1100
|
if (active.record.terminalOutcome) {
|
|
1069
1101
|
if (active.record.terminalOutcome.status !== 'review-ready') {
|
|
1070
1102
|
return publicOutcome(active.record.terminalOutcome);
|
|
@@ -1413,6 +1445,11 @@ export class RunIssue {
|
|
|
1413
1445
|
reviewScope = await this.reviewValidationScope(active, frozenCriteria, checkPolicy.checks, config.deny.readPaths);
|
|
1414
1446
|
}
|
|
1415
1447
|
catch (error) {
|
|
1448
|
+
if (error instanceof ReviewTargetCredentialError) {
|
|
1449
|
+
return this.terminal(active, { status: 'blocked', kind: 'safety', resumable: false, blocker: {
|
|
1450
|
+
kind: 'safety', summary: error.message, attempted: ['Inspect exact candidate diff for credential material.'], resumable: false,
|
|
1451
|
+
} }, 'review-target-credential-evidence');
|
|
1452
|
+
}
|
|
1416
1453
|
return this.invokedFailure(active, 'review-target-unavailable', error instanceof Error ? error.message : undefined);
|
|
1417
1454
|
}
|
|
1418
1455
|
const configuredChecks = Object.entries(reviewScope.checks);
|
|
@@ -1731,6 +1768,57 @@ export class RunIssue {
|
|
|
1731
1768
|
}
|
|
1732
1769
|
}
|
|
1733
1770
|
}
|
|
1771
|
+
async retryTerminalProof(active, config) {
|
|
1772
|
+
const reject = async (reason) => {
|
|
1773
|
+
const evidence = await this.dependencies.writeEvidence({ runId: active.record.runId, code: 'proof-retry-not-eligible', summary: reason });
|
|
1774
|
+
return { status: 'not-eligible', reason, evidencePath: evidence.path };
|
|
1775
|
+
};
|
|
1776
|
+
const record = active.record;
|
|
1777
|
+
const candidate = this.dependencies.git.candidateV2;
|
|
1778
|
+
if (record.lifecycle !== 'internal-error' || record.terminalOutcome?.status !== 'internal-error'
|
|
1779
|
+
|| record.terminalOutcome.code !== 'acceptance-proof-internal-failure'
|
|
1780
|
+
|| !record.deliveryAuthority || !record.candidateBinding || !record.implementationResult || !candidate
|
|
1781
|
+
|| record.pendingEffect || record.activeAttempt || record.candidateMaterialization || record.proofReceipt
|
|
1782
|
+
|| record.reviewFeedback?.activeBatch) {
|
|
1783
|
+
return reject('Proof retry requires a quiescent, unpublished candidate without passed proof or pending effects.');
|
|
1784
|
+
}
|
|
1785
|
+
if (!await this.authorized(active, config))
|
|
1786
|
+
return reject('The existing trusted issue claim must still authorize execution.');
|
|
1787
|
+
if (await this.dependencies.pullRequests.findOpen({ headBranch: record.branchName, baseBranch: config.github.baseBranch })) {
|
|
1788
|
+
return reject('Proof retry cannot reopen a run with an open pull request.');
|
|
1789
|
+
}
|
|
1790
|
+
await this.dependencies.verifyWorkflowGeneration(record.workflowGeneration);
|
|
1791
|
+
const worktree = await this.dependencies.git.inspectWorktree({
|
|
1792
|
+
worktreePath: record.worktreePath, branchName: record.branchName, baseSha: expectedImplementationHead(record),
|
|
1793
|
+
});
|
|
1794
|
+
if (worktree !== 'matching')
|
|
1795
|
+
return reject('The existing issue worktree identity or HEAD has changed.');
|
|
1796
|
+
const pin = await candidate.inspectPin(record.candidateBinding);
|
|
1797
|
+
if (pin.kind === 'failed')
|
|
1798
|
+
return this.mapCandidateFailure(active, pin.code);
|
|
1799
|
+
if (pin.value !== 'matching')
|
|
1800
|
+
return reject('The immutable candidate pin is missing or diverged.');
|
|
1801
|
+
let directReview = record.directReview;
|
|
1802
|
+
if (directReview) {
|
|
1803
|
+
if (directReview.status !== 'terminal' || directReview.review.disposition !== 'active') {
|
|
1804
|
+
return reject('The retained Review cannot resume validation of this candidate.');
|
|
1805
|
+
}
|
|
1806
|
+
const { terminalCode: _code, terminalOutcome: _outcome, ...retained } = structuredClone(directReview);
|
|
1807
|
+
directReview = { ...retained, status: 'active' };
|
|
1808
|
+
}
|
|
1809
|
+
if (!await this.authorized(active, config))
|
|
1810
|
+
return reject('Issue execution authority changed during proof retry validation.');
|
|
1811
|
+
await this.dependencies.writeEvidence({
|
|
1812
|
+
runId: record.runId, code: 'acceptance-proof-retry-authorized',
|
|
1813
|
+
summary: canonicalJson({ previousOutcome: record.terminalOutcome, previousProofId: record.proofId ?? null,
|
|
1814
|
+
candidateBindingId: record.candidateBinding.bindingId, cycle: record.cycle }),
|
|
1815
|
+
});
|
|
1816
|
+
return { active: await this.persist(active, {
|
|
1817
|
+
lifecycle: 'checking', terminalOutcome: undefined, outcomeEvidenceId: undefined,
|
|
1818
|
+
checks: [], checkedChangeSha256: undefined, proofId: undefined, proofExecution: undefined, proofReceipt: undefined,
|
|
1819
|
+
...(directReview ? { directReview } : {}),
|
|
1820
|
+
}) };
|
|
1821
|
+
}
|
|
1734
1822
|
async initializeClaimedRun(active, issue) {
|
|
1735
1823
|
if (active.record.lifecycle !== 'claimed'
|
|
1736
1824
|
|| active.record.activeAttempt
|
|
@@ -2514,9 +2602,10 @@ export class RunIssue {
|
|
|
2514
2602
|
if (!binding)
|
|
2515
2603
|
throw new Error('review scope requires candidate identity');
|
|
2516
2604
|
const completeDiff = await this.dependencies.git.diffTrees(active.record.worktreePath, active.record.baseSha, binding.candidateTreeSha);
|
|
2605
|
+
if (containsCredentialEvidence(completeDiff.patch))
|
|
2606
|
+
throw new ReviewTargetCredentialError();
|
|
2517
2607
|
if (completeDiff.changedFiles.length === 0 || completeDiff.patch.trim().length === 0
|
|
2518
2608
|
|| Buffer.byteLength(completeDiff.patch, 'utf8') > MAX_REVIEW_PATCH_BYTES
|
|
2519
|
-
|| containsCredentialEvidence(completeDiff.patch)
|
|
2520
2609
|
|| completeDiff.changedFiles.some((path) => findDeniedPathMatch(path, deniedPaths))) {
|
|
2521
2610
|
throw new Error('exact complete Review target is unavailable');
|
|
2522
2611
|
}
|
|
@@ -2597,32 +2686,42 @@ export class RunIssue {
|
|
|
2597
2686
|
reviewScope = suppliedScope ?? await this.reviewValidationScope(active, frozenCriteria, resolveIssueCheckPolicy(active.record.issueSnapshot.body, config.checks).checks, config.deny.readPaths);
|
|
2598
2687
|
}
|
|
2599
2688
|
catch (error) {
|
|
2689
|
+
if (error instanceof ReviewTargetCredentialError) {
|
|
2690
|
+
return this.terminal(active, { status: 'blocked', kind: 'safety', resumable: false, blocker: {
|
|
2691
|
+
kind: 'safety', summary: error.message, attempted: ['Inspect exact candidate diff for credential material.'], resumable: false,
|
|
2692
|
+
} }, 'review-target-credential-evidence');
|
|
2693
|
+
}
|
|
2600
2694
|
return this.invokedFailure(active, 'review-scope-unavailable', error instanceof Error ? error.message : undefined);
|
|
2601
2695
|
}
|
|
2602
2696
|
let reportRepair;
|
|
2603
2697
|
const retainedAttempt = active.record.activeAttempt;
|
|
2604
2698
|
const retainedReview = active.record.directReview;
|
|
2605
|
-
if (retainedReview?.review.reportRepairs
|
|
2606
|
-
const
|
|
2607
|
-
|
|
2699
|
+
if (retainedReview?.review.reportRepairs) {
|
|
2700
|
+
const original = retainedReview.review.reportCorrection
|
|
2701
|
+
?? (retainedAttempt?.stage === 'adopted' ? retainedAttempt.result : undefined);
|
|
2702
|
+
if (!original)
|
|
2703
|
+
return this.terminal(active, { status: 'internal-error', code: 'review-report-correction-result-missing' });
|
|
2704
|
+
const stored = await this.dependencies.inspectAttemptResult(original.path);
|
|
2705
|
+
if (!stored || stored.sha256 !== original.sha256) {
|
|
2608
2706
|
return this.invokedFailure(active, 'direct-review-report-result-unavailable', 'The exact malformed Review result is not currently observable; report-only recovery remains fenced.');
|
|
2609
2707
|
}
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
}
|
|
2708
|
+
if (retainedReview.review.reportRepairs >= 2) {
|
|
2709
|
+
return this.terminal(active, { status: 'blocked', kind: 'external', resumable: false, blocker: {
|
|
2710
|
+
kind: 'external', resumable: false, summary: 'Review report correction limit reached; retained candidate and report require operator recovery.',
|
|
2711
|
+
attempted: ['Preserved the candidate, checks, proof and retained report without launching another reviewer.'],
|
|
2712
|
+
} });
|
|
2713
|
+
}
|
|
2714
|
+
let context;
|
|
2715
|
+
try {
|
|
2716
|
+
context = validateReviewContext(retainedReview.review.validationContext);
|
|
2717
|
+
if (context.targetRevision !== retainedReview.targetRevision || context.targetFingerprint !== retainedReview.targetFingerprint
|
|
2718
|
+
|| context.reviewerSessionId !== retainedReview.review.reviewerSessionId)
|
|
2719
|
+
throw new Error('review context drift');
|
|
2720
|
+
}
|
|
2721
|
+
catch {
|
|
2722
|
+
return this.terminal(active, { status: 'internal-error', code: 'review-validation-context-missing' });
|
|
2723
|
+
}
|
|
2724
|
+
const validation = validateCompletedReport('code-review', retainedAttempt?.attemptId ?? active.record.runId, stored.bytes, context);
|
|
2626
2725
|
if (validation.status !== 'invalid' || !validation.repairInput) {
|
|
2627
2726
|
return this.terminal(active, { status: 'blocked', kind: 'safety', resumable: false }, 'direct-review-retained-result-diverged');
|
|
2628
2727
|
}
|
|
@@ -2631,7 +2730,12 @@ export class RunIssue {
|
|
|
2631
2730
|
originalReportBytes: Buffer.from(validation.repairInput.originalReportBytes),
|
|
2632
2731
|
diagnostic: validation.findings[0] ?? 'review report is invalid',
|
|
2633
2732
|
};
|
|
2634
|
-
|
|
2733
|
+
if (!retainedReview.review.reportCorrection) {
|
|
2734
|
+
active = await this.persist(active, { directReview: { ...retainedReview,
|
|
2735
|
+
review: { ...retainedReview.review, reportCorrection: original } } });
|
|
2736
|
+
}
|
|
2737
|
+
if (retainedAttempt?.stage === 'adopted')
|
|
2738
|
+
active = await this.clearAttempt(active);
|
|
2635
2739
|
}
|
|
2636
2740
|
while (true) {
|
|
2637
2741
|
const directReview = active.record.directReview;
|
|
@@ -2701,10 +2805,7 @@ export class RunIssue {
|
|
|
2701
2805
|
frozenCriteria: reviewScope.criteria,
|
|
2702
2806
|
deliveryAuthority: structuredClone(active.record.deliveryAuthority),
|
|
2703
2807
|
defects: structuredClone(directReview.review.defects),
|
|
2704
|
-
reviewFocus: [
|
|
2705
|
-
'candidate-proof-binding', 'correctness', 'duplicate-ownership', 'maintainability',
|
|
2706
|
-
'repository-standards', 'requirements', 'tests', 'zero-legacy',
|
|
2707
|
-
],
|
|
2808
|
+
reviewFocus: [...COMPLETE_REVIEW_COVERAGE],
|
|
2708
2809
|
workflowGeneration: structuredClone(active.record.workflowGeneration),
|
|
2709
2810
|
repairOnly: reportRepair !== undefined,
|
|
2710
2811
|
originalReportSha256: reportRepair?.originalReportSha256 ?? null,
|
|
@@ -2714,6 +2815,12 @@ export class RunIssue {
|
|
|
2714
2815
|
onPrepared: async (invocation) => {
|
|
2715
2816
|
if (!active.record.directReview)
|
|
2716
2817
|
throw new Error('direct review disappeared before prepare');
|
|
2818
|
+
const validationContext = validateReviewContext(invocation.validationContext);
|
|
2819
|
+
if (validationContext.targetFingerprint !== directReview.targetFingerprint || validationContext.targetRevision !== directReview.targetRevision
|
|
2820
|
+
|| validationContext.reviewerSessionId !== reviewerSessionId)
|
|
2821
|
+
throw new Error('review validation context drift');
|
|
2822
|
+
active = await this.persist(active, { directReview: { ...active.record.directReview,
|
|
2823
|
+
review: { ...active.record.directReview.review, validationContext } } });
|
|
2717
2824
|
if (invocation.attemptId !== active.record.activeAttempt?.attemptId
|
|
2718
2825
|
|| invocation.operation !== 'code-review'
|
|
2719
2826
|
|| invocation.reviewerSessionId !== reviewerSessionId)
|
|
@@ -2759,6 +2866,8 @@ export class RunIssue {
|
|
|
2759
2866
|
completeReview.review.defects = structuredClone(result.report.defects);
|
|
2760
2867
|
completeReview.review.reviewerSessionId = this.dependencies.createReviewSessionId();
|
|
2761
2868
|
completeReview.review.reportRepairs = 0;
|
|
2869
|
+
delete completeReview.review.validationContext;
|
|
2870
|
+
delete completeReview.review.reportCorrection;
|
|
2762
2871
|
completeReview.review.transportRetries = 0;
|
|
2763
2872
|
active = await this.persist(settledExecution.active, {
|
|
2764
2873
|
lifecycle: 'checking',
|
|
@@ -2819,7 +2928,15 @@ export class RunIssue {
|
|
|
2819
2928
|
const current = active.record.directReview;
|
|
2820
2929
|
if (!current)
|
|
2821
2930
|
return this.terminal(active, { status: 'internal-error', code: 'direct-review-result-orphaned' });
|
|
2822
|
-
active = await this.adoptValidationTransition(active, result.originalReportSha256, projectValidationReviewReportRepair(active.record));
|
|
2931
|
+
active = await this.adoptValidationTransition(active, result.originalReportSha256, projectValidationReviewReportRepair(active.record, { path: active.record.activeAttempt.resultPath, sha256: result.originalReportSha256 }));
|
|
2932
|
+
if (active.record.directReview.review.reportRepairs >= 2) {
|
|
2933
|
+
const repeated = reportRepair?.originalReportSha256 === result.originalReportSha256;
|
|
2934
|
+
return this.terminal(active, { status: 'blocked', kind: 'external', resumable: false, blocker: {
|
|
2935
|
+
kind: 'external', resumable: false,
|
|
2936
|
+
summary: `Review report correction failed${repeated ? ' without changing the report' : ''}: ${result.diagnostic}. Candidate and evidence are retained.`,
|
|
2937
|
+
attempted: ['Reviewed the candidate and attempted one report-only correction.'],
|
|
2938
|
+
} });
|
|
2939
|
+
}
|
|
2823
2940
|
return this.invokedFailure(active, 'direct-review-report-retryable', 'The review report was malformed; a later bounded invocation may retry the same immutable candidate.');
|
|
2824
2941
|
}
|
|
2825
2942
|
if (result.kind === 'transport-failed') {
|
|
@@ -3352,7 +3469,9 @@ function terminalReportSnapshot(record, outcome) {
|
|
|
3352
3469
|
: outcome.status === 'blocked'
|
|
3353
3470
|
? outcome.resumable
|
|
3354
3471
|
? 'Resolve the stated blocker, then rerun the same issue.'
|
|
3355
|
-
:
|
|
3472
|
+
: outcome.kind === 'external'
|
|
3473
|
+
? 'Inspect the retained report and Runner diagnostics, correct the reported condition, then explicitly retry the retained work.'
|
|
3474
|
+
: 'Provide the missing decision or authority in this issue before continuing.'
|
|
3356
3475
|
: outcome.status === 'internal-error'
|
|
3357
3476
|
? 'Inspect the package-owned evidence for the code above, correct the runner condition, and retry the same issue.'
|
|
3358
3477
|
: 'Re-add the authorization label and rerun the same issue if the work should continue.';
|