Skip to content

refactor(deep-scan): compose ordinary scans and merge their results - #939

Draft
mldangelo-oai wants to merge 36 commits into
mainfrom
mdangelo/codex/simplify-deep-scan
Draft

mldangelo-oai wants to merge 36 commits into
mainfrom
mdangelo/codex/simplify-deep-scan

Conversation

@mldangelo-oai

@mldangelo-oai mldangelo-oai commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Deep Scan runs ordinary Standard scans through the shared SDK lifecycle, then merges their completed reports. This removes the separate worker and reducer lifecycle while retaining saved settings, stopping rules, original findings and child coverage.

Changes

  • Run ordinary scans in parallel batches and use their saved records plus a parent checkpoint for progress, cancellation and resume.
  • Preserve accepted finding identities, archived originals and incomplete child coverage. The shared runner finalizes and seals the parent report before Deep Scan returns.
  • Keep native client cleanup awaited while preserving the completed result or original startup error if cleanup fails.
  • Treat whitespace-only CODEX_HOME overrides as unset. Preserve other paths and the caller's environment.
  • Align shared guidance with ordinary scan permissions and sealed Deep Scan results, without a second completion call.

Testing

Validation of cleanup head 6754e4d5:

  • Ruff 0.16.6 check and format check passed.
  • The exact SDK build:ci compiler command passed through the pinned compiler entrypoint. Plugin source compatibility passed, along with all nine tests of that checker.
  • Six selected native behavior tests passed, including cleanup failure, joining/draining, cancellation, saved settings, and synthetic fresh/resumed SDK child processes.
  • The SDK and bundled plugin builds, static package archive contract, and finite archive version/schema checks passed.
  • An initial package-manager command automatically installed dependencies. That attempt and its downstream results are excluded from the frozen-dependency verification above.

Full regression, the full installed-package smoke suite and Windows verification remain pending. The synthetic child-process fixture skips Windows; the shared home-selection matrix remains enabled there. These checks do not establish performance or detection-quality improvements.

Risk and rollout

This remains a draft. Public options, stopping defaults and saved-scan contracts are preserved. The plugin and bundled SDK must ship together. The whitespace-only home issue also existed in the previous executor; this corrects the replacement path.

The final commit includes [skip ci] to defer automatic broad-suite execution for this draft update. Only the scoped checks above are claimed; required CI checks remain pending. Existing Windows failures and cancellations have unresolved causes, and no timeout increase is included.

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, description, commits, changes, comments, logs, screenshots, attachments, and links for public disclosure.

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: b153d6080e

ℹ️ 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".

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 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-17T03:24:41.727272Z 6754e4d Manual request
ℹ️ 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.

Copy link
Copy Markdown
Collaborator 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: 53b4f2a23f

ℹ️ 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/deep-scan.ts Outdated
Comment on lines +363 to +364
reportCompletedCost(pass.directory, result.cost);
executionSignal.throwIfAborted();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Merge the completed pass before propagating its cost abort

When the first completed child pushes the combined cost above maxCostUsd, reportCompletedCost synchronously triggers the parent's cost abort, so this check throws after adding the child to accepted but before mergePending can consolidate or publish it. Because no merge turn has started yet, #run also has no budgetRecovery.threadId, skips complete-budget-exhausted-scan, and fails the parent instead of returning the completed child's findings with partial coverage. Defer propagation until the accepted pass is merged and published.

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

Useful? React with 👍 / 👎.

Comment thread sdk/typescript/src/api.ts
Comment on lines +2591 to +2593
await Promise.all(
staged.map((path) => artifactWriter!.remove(path)),
);

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 Do not fail a published scan on staging cleanup

If either staged-file removal fails after write-scan-draft has successfully published the canonical parent draft, this finally block rejects the publish operation. runDeepScans then marks the composition failed and ultimately throws even if its catch-path publication retry succeeds, so a transient cleanup error turns an otherwise valid Deep Scan into a failed scan. Treat removal of these now-unreferenced staging files as best-effort cleanup and report the error without replacing the scan result.

Useful? React with 👍 / 👎.

Comment thread sdk/typescript/src/deep-scan.ts Outdated
Comment on lines +194 to +196
if (record.progress.status === "complete")
reportCompletedCost(pass.directory, record.cost ?? null);
else if (record.cost) input.onCost(pass.directory, record.cost);

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 Restore failed pass state when resuming

If the process stops after fail-scan commits a child's failed status but before pass.failed, consecutiveErrors, and the checkpoint are saved, a resume observes the child as failed here but never copies that status back into the checkpoint. The pass is then excluded from unfinished without contributing to the consecutive-error state; at the run cap this can make an all-failed Deep Scan proceed to an empty successful aggregate, and before the cap it incorrectly resets the configured failure streak. Reconcile persisted failed child records into the pass/error state during refresh.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator 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: 690cd7b6fc

ℹ️ 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 on lines +394 to +396
...(latestCost
? ["--cost-json", JSON.stringify(latestCost)]
: []),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Stop when a failed pass has no verifiable cost

When a discovery pass exhausts its retries without ever emitting onCost, this branch persists no cost and marks the pass failed without reporting null or raising ScanCostTrackingError; the resume path likewise ignores failed records with no stored cost. A cost-limited parent therefore treats its combined cost as complete and may continue or finish after that pass consumed unmetered tokens, bypassing the requested --max-cost. Treat the absent failed-pass cost as unavailable just as for a completed child.

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

Useful? React with 👍 / 👎.

Comment on lines +224 to +226
const codexHome = await realpath(
environment.CODEX_HOME ?? join(homedir(), ".codex"),
);

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 an empty CODEX_HOME as unset

When the MCP process inherits CODEX_HOME="", this expression passes the empty string to realpath, which fails with ENOENT and prevents every native Deep Scan from starting. The normal SDK path resolver uses the established trimmed-value-or-~/.codex behavior, so this native path should do the same rather than interpreting an empty environment variable as a filesystem path.

AGENTS.md reference: AGENTS.md:L30-L33

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator 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: f69cb00cc3

ℹ️ 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 on lines +12 to +14
if (codexHome !== undefined && codexHome.length > 0) {
// Resolve symlink/.. paths before consumers normalize them or change cwd.
environment.CODEX_HOME = await fs.realpath(codexHome);

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 whitespace-only CODEX_HOME as unset

Fresh evidence after the earlier empty-value report: this replacement still calls realpath when CODEX_HOME contains only whitespace, so native Deep Scan fails with ENOENT while the established configuredCodexHome path trims the value and falls back to ~/.codex. Trim before deciding whether to canonicalize, and preserve the same behavior in worker-launch tests.

AGENTS.md reference: AGENTS.md:L30-L34

Useful? React with 👍 / 👎.

Comment thread sdk/typescript/src/api.ts
@@ -2395,6 +3080,7 @@ export class CodexSecurity {
} finally {
budgetAbortController.abort();
deepProgressTracker?.stop();
releaseExecution?.();

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 Prevent lock cleanup from replacing the scan outcome

If the execution-lock release throws—for example, the new Windows release explicitly throws when the native handle close returns an error—this unguarded call runs in finally outside the surrounding catch and replaces either a successfully sealed result or the scan's real failure. Handle this cleanup like the guarded cleanup immediately below so callers do not receive a lock-close error after their scan has already completed.

Useful? React with 👍 / 👎.

Keep client cleanup awaited while preserving the original scan outcome.
Remove the optional execution-lock guard after reviewing its native producer.

Automatic workflow execution is deferred for this draft update; the skip
marker is not a passing CI result. Required verification remains separate.
@mldangelo-oai

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 6754e4d554

ℹ️ 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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant