Skip to content

[dnm]: test cancellation - #524

Draft
jamieQ wants to merge 3 commits into
mainfrom
test/cancel-ii
Draft

[dnm]: test cancellation#524
jamieQ wants to merge 3 commits into
mainfrom
test/cancel-ii

Conversation

@jamieQ

@jamieQ jamieQ commented Aug 27, 2026

Copy link
Copy Markdown
Member

testing

Abort in-flight analysis, preserve partial findings and usage, and finalize findings artifacts with a cancelled outcome. Keep analyze/report publication ownership intact and conclude active checks consistently.

Co-Authored-By: GPT-5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
warden-prod Skipped Skipped Aug 27, 2026 7:29pm

Request Review

Comment on lines +47 to +66
if: ${{ cancelled() }}
env:
FINDINGS_FILE: ${{ steps.warden-analyze.outputs.findings-file }}
run: |
if [ -z "$FINDINGS_FILE" ]; then
echo "Analyze was cancelled without producing a findings file" >&2
exit 1
fi

FINDINGS_FILE="$FINDINGS_FILE" node --input-type=module -e '
const fs = await import("node:fs");
const path = process.env.FINDINGS_FILE;
const findings = JSON.parse(fs.readFileSync(path, "utf8"));
if (findings.outcome !== "cancelled") {
throw new Error(`Expected cancelled outcome, received ${findings.outcome}`);
}
if (!fs.existsSync(`${path}.done`)) {
throw new Error(`Missing finalization marker: ${path}.done`);
}
console.log(`Preserved ${findings.summary.totalFindings} finding(s)`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cancellation after Analyze completes makes Verify reject a successful artifact

When concurrency cancellation arrives after Analyze has written its normal successful artifact but before Report runs, the job-level cancelled() guard executes Verify against that artifact. Because successful analyze output has no outcome: "cancelled", Verify fails, and the following broad cancellation guard can archive the valid artifact as cancelled-findings; gate these steps on an Analyze cancellation that corresponds to a cancelled artifact rather than job cancellation alone.

Evidence
  • The PR concurrency group uses cancel-in-progress: true, so a newer synchronize run can cancel the prior job between the sequential Analyze and Report steps.
  • Normal runAnalyzeMode completion calls writeFindingsOutput without an outcome, while finalizeCancelledPRRun is the path that writes outcome: 'cancelled'.
  • Verify runs before Report under only cancelled(), reads the Analyze output, and throws when the successful artifact's outcome is missing.
  • Upload also uses only cancelled() plus the non-empty findings-file output, so the cancellation state remains active after Verify fails and the successful artifact can be uploaded under a cancelled name.

Identified by Warden · code-review · WMS-WUJ

Comment on lines +2547 to +2592
if (cancellation.requested && inputs.mode !== 'report') {
await finalizeCancelledPRRun({
...buildCancelledPRFinalizationBase(inputs, initResult),
results: [],
publish: inputs.mode === 'run',
failOnWriteError: inputs.mode === 'analyze',
});
span.setAttribute('warden.finding.count', 0);
return;
}

if (inputs.mode === 'analyze') {
return runAnalyzeMode(inputs, initResult, span);
return runAnalyzeMode(inputs, initResult, span, cancellation);
}

if (inputs.mode === 'report') {
return runReportMode(octokit, inputs, initResult, repoPath, span);
return runReportMode(octokit, inputs, initResult, repoPath, span, cancellation);
}

const { coreCheckId, previousReviewInfo } = await Sentry.startSpan(
{ op: 'workflow.setup', name: 'setup github state' },
() => setupGitHubState(octokit, context, postChecks),
);

const finalizeCancelledRun = async (
results: TriggerResult[] = [],
findingObservations: FindingObservation[] = [],
): Promise<boolean> => {
if (!cancellation.requested) {
return false;
}
await cancelCoreCheck(octokit, context, coreCheckId, results, postChecks);
const outputs = await finalizeCancelledPRRun({
...buildCancelledPRFinalizationBase(inputs, initResult),
results,
findingObservations,
publish: true,
});
span.setAttribute('warden.finding.count', outputs.findingsCount);
logAction(`Analysis cancelled: preserved ${formatCancelledPreservation(outputs.findingsCount, results)}`);
return true;
};

if (await finalizeCancelledRun()) {
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-execution cancel finalizes with empty results, dropping matched triggers

When cancellation is requested before trigger execution, finalizeCancelledRun/finalizeCancelledPRRun are called with results: [] even if matchedTriggers is non-empty, so cancelled findings omit pending triggerResults/skippedTriggers for those matched skills.

Evidence
  • The early cancel path at lines 2547–2555 and the first finalizeCancelledRun() call at 2590–2592 both finalize with results defaulting to [] before executeAllTriggers runs.
  • executeAllTriggers only synthesizes pending results for undispatched matched triggers when cancellation is requested (lines 699–706); that path is never reached here.
  • finalizeCancelledPRRun builds triggerResults/skippedTriggers only from options.results via toReplayTriggerResults/toUnfinishedSkippedTriggers, so matched-but-never-dispatched triggers disappear from the cancelled artifact.
  • The same contract is enforced after dispatch (pending: true) and covered by the mid-execution cancel test expecting pending triggerResults/skippedTriggers.

Identified by Warden · code-review · V7P-XNY

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant