Skip to content

fix(sdk): preserve canceled scan state - #735

Open
Hughhhhcoder wants to merge 8 commits into
openai:mainfrom
Hughhhhcoder:codex/codex-security-scan-cancel
Open

fix(sdk): preserve canceled scan state#735
Hughhhhcoder wants to merge 8 commits into
openai:mainfrom
Hughhhhcoder:codex/codex-security-scan-cancel

Conversation

@Hughhhhcoder

Copy link
Copy Markdown
Contributor

Summary

Fixes #44. When a caller cancels a registered SDK scan, the scan history is
currently finalized with fail-scan, so an intentional cancellation is stored
as a failure instead of a cancellation.

Changes

  • Route caller- or client-close-induced ScanInterruptedError values through
    cancel-scan.
  • Keep fail-scan for ordinary failures, internal tracking failures, and
    ScanCostLimitExceededError so those errors retain their message and cost
    metadata.
  • Add an orchestration regression that verifies the workbench command and
    arguments for a canceled scan.

Testing

  • bun test --timeout 30000 tests-ts/api.test.ts — 139 passed, 2 skipped, 0 failed.
  • bun test --timeout 30000 tests-ts/scan-recovery.test.ts — 40 passed, 0 failed.
  • pnpm --ignore-workspace run lint — passed.
  • pnpm --ignore-workspace --dir ../../plugins/codex-security/mcp-app run typecheck — passed.
  • pnpm --ignore-workspace run generate:models:check — passed.
  • pnpm --ignore-workspace run format — passed.
  • git diff --check — passed.

Risk and rollout

This changes only the persisted lifecycle command for explicit caller/client
cancellation. Ordinary failures and cost-limit recovery retain the existing
failure path and metadata. No public API or scan input contract changes.

Public disclosure review

  • No customer, partner, prospect, or user identities, data, or identifying details are included.
  • No credentials, personal data, private source, scan findings, or nonpublic links or tickets are included.
  • I reviewed the branch name, title, commits, changes, comments, logs, screenshots, attachments, and links for public disclosure.

@github-actions github-actions Bot added the bug Something isn't working label Aug 29, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T10:09:36.771217Z 3854a75 Manual request
🔒 Security Review Completed 2026-09-03T10:09:46.828855Z 3854a75 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Hughhhhcoder

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 53a8c9f052

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sdk/typescript/src/api.ts Outdated
@Hughhhhcoder

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 67afadc3ba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sdk/typescript/src/api.ts Outdated
@Hughhhhcoder

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 3394120d06

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@sylvesterkaczmarek sylvesterkaczmarek left a comment

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.

canceled is inferred from the signal's current state, not from the exception that ended the scan. If a real scan failure occurs and abort/close races before this bookkeeping block, the failure is recorded as canceled and its message/cost are dropped. Use cancel-scan only for a cancellation-derived failure, and add a fail-then-abort race test.

@Hughhhhcoder

Copy link
Copy Markdown
Contributor Author

Addressed in commit 13ca814c. Cancellation is now selected only for a cancellation-derived failure: direct interruption/AbortError failures (including wrapped causes) still use cancel-scan, while an ordinary failure that races with caller cancellation remains on fail-scan and keeps its message. The existing cancellation tests plus the new race regression pass (3 targeted tests); TypeScript, Prettier, model-generation, and diff checks pass.

@Hughhhhcoder

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 13ca814c11

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sdk/typescript/src/api.ts Outdated
}
return (
current === signal.reason ||
(isRecord(current) && current["name"] === "AbortError")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the first internal abort during cancellation races

When maxCostUsd tracking aborts the combined signal first, the Codex stream can surface a nested AbortError; if the caller signal or client close then aborts before this catch classifies the failure, the external-signal check passes and this name-only branch returns true even though signal.reason remains the original tracking error. The scan is consequently persisted with cancel-scan, losing the tracking failure message and cost instead of using fail-scan. Fresh evidence beyond the prior tracking-error fix is this nested AbortError path, which bypasses the first-abort origin retained by AbortSignal.any.

AGENTS.md reference: sdk/typescript/AGENTS.md:L23-L25

Useful? React with 👍 / 👎.

…scan-cancel

# Conflicts:
#	sdk/typescript/src/api.ts
@Hughhhhcoder

Copy link
Copy Markdown
Contributor Author

@codex review

@Hughhhhcoder

Copy link
Copy Markdown
Contributor Author

Merged latest origin/main in commit 6eb5e4db to clear the branch conflict. The cancellation-race fix and regression test are unchanged. Re-validated the focused API tests (3 passed), TypeScript lint, Prettier check, generated-model check, and plugin build.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6eb5e4dbc8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sdk/typescript/src/api.ts
Comment on lines +1838 to +1842
const canceled =
signal.aborted &&
(options.signal?.aborted === true ||
this.#abortController.signal.aborted) &&
isCancellationDerivedFailure(failure, signal);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Treat client-close errors as cancellation

When close() aborts an already registered scan just as an awaited operation completes normally, the subsequent checkOpen() throws CodexSecurity is closed. before throwIfAborted() can produce a cancellation-derived error. That ordinary error fails this predicate, so the scan is still persisted through fail-scan even though client closure caused it to stop. Classify this closed-client path as cancellation while continuing to preserve any earlier internal failure.

AGENTS.md reference: sdk/typescript/AGENTS.md:L23-L25

Useful? React with 👍 / 👎.

@Hughhhhcoder

Copy link
Copy Markdown
Contributor Author

Addressed the client-close race review in commit ad93cd53. A CodexSecurity is closed. sentinel is now recognized as cancellation only when it is the observed failure, so close-triggered scans use cancel-scan while earlier ordinary/internal failures still use `fail-scan. Added a deterministic regression test for closing while feedback is in flight.

Validation: 5 focused API tests passed, TypeScript lint, Prettier, generated-model check, and plugin build passed.

@Hughhhhcoder

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ad93cd531d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sdk/typescript/src/api.ts
try {
await workbench({ ...activeScan.options, signal: undefined }, [
"fail-scan",
canceled ? "cancel-scan" : "fail-scan",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Route canceled mock scans through cancel-scan

When run() is called with { mock: true }, the dispatch at the start of #run bypasses this lifecycle selection, and #runMock still unconditionally invokes fail-scan in its catch block. A caller abort after onScanStarted therefore leaves the registered mock scan without canceled_at, while the equivalent real scan now uses cancel-scan; apply the same cancellation classification to the mock path so persisted scan state remains consistent.

AGENTS.md reference: sdk/typescript/AGENTS.md:L23-L23

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in commit 647d7266. Mock scans now use the same cancellation classification as real scans: cancellation-derived failures call cancel-scan without a failure message; ordinary failures still call fail-scan with the sanitized message. Updated the existing abort regression to assert the canceled terminal state. Focused API/mock tests, lint, format, model-generation, plugin build, and diff checks pass.

@Hughhhhcoder

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 647d72662f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sdk/typescript/src/api.ts Outdated
signal: AbortSignal,
): boolean {
let current = failure;
for (let depth = 0; depth < 8; depth += 1) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Remove the arbitrary cutoff from cancellation traversal

When a caller aborts a registered scan with an AbortSignal.reason containing at least seven nested ScanInterruptedError causes, throwIfAborted adds another wrapper and this loop exits before reaching the matching reason or AbortError. The scan is then persisted with fail-scan even though caller cancellation caused it; traverse until a non-ScanInterruptedError cause, using cycle detection rather than a fixed depth.

AGENTS.md reference: sdk/typescript/AGENTS.md:L24-L24

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in commit 3854a75f. Cancellation classification now traverses nested ScanInterruptedError causes until it reaches the underlying reason, with cycle detection and no arbitrary depth cutoff. The existing ordinary-failure and cost-limit guards remain fail-closed. Added a regression with ten nested interruption causes; focused API/mock tests, lint, format, model-generation, plugin build, and diff checks pass.

@Hughhhhcoder

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 3854a75fc9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Canceling a scan with Ctrl-C can leave it marked as RUNNING indefinitely

2 participants