Skip to content

feat: measure security scan tokens across workers - #265

Open
mldangelo-oai wants to merge 6 commits into
mdangelo/codex/sync-scan-lifecycle-metadatafrom
mdangelo/codex/sync-cross-worker-token-usage
Open

feat: measure security scan tokens across workers#265
mldangelo-oai wants to merge 6 commits into
mdangelo/codex/sync-scan-lifecycle-metadatafrom
mdangelo/codex/sync-cross-worker-token-usage

Conversation

@mldangelo-oai

Copy link
Copy Markdown
Collaborator

Summary

  • Add cross-worker scan token accounting and expose measured usage through the workbench, scan history, and reporting guidance.
  • Ship the new usage helper through the public plugin projection without weakening existing public-only runtime protections.

Stack

  • Intentionally stacked on mdangelo/codex/sync-scan-lifecycle-metadata so this review contains only its atomic upstream change.
  • Prerequisite for the pending 0.1.6 release in release: bump Codex Security to 0.1.6 #244; package version is unchanged.

Verification

  • Parsed every bundled workbench Python module.
  • Verified all 95 declared plugin payload files exist.
  • Initialized a fresh private workbench SQLite database successfully.
  • Passed Prettier for all 5 changed formatted files.
  • The complete reconstructed sync passed 775 tests (5 platform/integration skips) and the installed-package smoke test.

@mldangelo-oai

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review the exact current head a5f8d0c78c587b41f278bb376d9ff398dd23655b and only this atomic PR's declared base-to-head diff.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Something went wrong. Try again later by commenting “@codex review”.

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

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: a5f8d0c78c

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

Copy link
Copy Markdown

Security review completed. No security issues were found in this pull request.

Reviewed commit: a5f8d0c78c

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@mldangelo-oai

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review the exact updated head 593c550e3ae8bffdde70e4e3fb70b808c9382da2. This additive update ships the exact generated MCP runtime from the same original upstream commit and preserves the atomic base-to-head diff.

@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: 593c550e3a

ℹ️ 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 +512 to +514
result["totalTokens"] = (
result["inputTokens"] - result["cachedInputTokens"] + result["outputTokens"]
)

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 Preserve cached input in the total token count

For every snapshot containing cached input, this replaces the producer's required total_tokens value with inputTokens - cachedInputTokens + outputTokens. The repository's existing parser defines the same payload's total as input + output (sdk/typescript/src/cost.ts:339-345), so a snapshot with 100 input, 40 cached, and 20 output is now reported as 80 total tokens instead of 120. Since completion guidance publishes this value to users, normal cache usage makes the new accounting materially underreport scan consumption.

Useful? React with 👍 / 👎.

Comment on lines +127 to +129
accepted_thread_ids.add(session.thread_id)
observed_thread_count += 1
_add_token_usage(total, session_usage)

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 Require token evidence before marking a thread observed

When a rollout has valid identity/ownership records but contains no valid token_count event in the scan window—for example, when a provider omits usage metadata—_read_rollout_usage returns an all-zero map without a warning, yet these lines count the thread as successfully observed. If every thread is in that state, the result is coverage: "complete" with zero tokens, causing the completion workflow to present zero as a measurement rather than the documented unavailable result.

Useful? React with 👍 / 👎.

return None
result: dict[str, int] = {}
for source_key, result_key in TOKEN_FIELDS.items():
value = usage.get(source_key, 0)

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 Honor the alternate cache-write token field

When total_token_usage uses the supported cache_write_tokens spelling, this lookup silently defaults cacheWriteInputTokens to zero. The existing parser for the same payload explicitly accepts both cache_write_input_tokens and cache_write_tokens (sdk/typescript/src/cost.ts:324-325), so affected scans are marked completely measured while underreporting cache-write usage; apply the same fallback here.

Useful? React with 👍 / 👎.

Comment on lines +168 to +170
SELECT DISTINCT sdk_thread_id
FROM deep_scan_workers
WHERE scan_id = ? AND sdk_thread_id IS NOT NULL

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 Retain thread IDs from every deep-scan retry

When a deep worker retries on a fresh SDK thread, deep_scan_workbench.py:989-1002 overwrites that worker's prior sdk_thread_id, while this query loads only the final value. A scan that succeeds after such a retry therefore omits all tokens consumed by the earlier attempt even though those tokens were scan-owned; persist attempt thread IDs separately or derive every historical attempt root when collecting usage.

Useful? React with 👍 / 👎.

Comment on lines +157 to +162
workspace = connection.execute(
"SELECT thread_id FROM workspaces WHERE id = ?",
(scan["workspace_id"],),
).fetchone()
if workspace is not None:
candidates.append(workspace["thread_id"])

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 Exclude the setup thread after continuation handoff

When continuation_thread_id differs from the workspace's original thread, both IDs are added as roots. The original workspace thread is long-lived, and for a root session without a recorded parent _read_rollout_usage treats ownership as established immediately, so any unrelated turns or subagents used on that setup thread while the scan continuation runs are included solely because their timestamps fall inside the scan window. Use the workspace thread only as a fallback when no continuation or deep owner is available, or establish a scan-specific ownership boundary for it.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector

Copy link
Copy Markdown

Security review completed. No security issues were found in this pull request.

Reviewed commit: 593c550e3a

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@mldangelo-oai

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review the exact updated head 9e616407d6cc7e219d52a2a4bd8e4ad0d03aa84b. The parent preserves repairable completion failures, and usage accounting now includes cached input in total tokens and honors alternate cache-write fields with a direct Python-boundary regression test.

@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: 9e616407d6

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

complete_scan.add_argument("--scan-id", required=True)
complete_scan.add_argument("--claim-token")
complete_scan.add_argument("--cost-json")
complete_scan.add_argument("--thread-id")

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 Forward the completion thread ID from the MCP handler

When an MCP completion runs from a thread that is not already persisted as the workspace, continuation, or deep-scan owner—such as a recovered continuation—this new argument is never populated. I inspected the bundled complete_codex_security_scan handler in mcp/server.mjs.br.part-*: unlike other thread-aware handlers, its callback does not read extra and invokes complete-scan with only --scan-id and --claim-token. Consequently collect_scan_usage cannot use the current thread as a root and can report unavailable or omit that thread and its descendants; pass threadIdFromExtra(extra) through --thread-id.

Useful? React with 👍 / 👎.

Comment on lines +509 to +512
if source_key in {"input_tokens", "output_tokens", "total_tokens"} and (
source_key not in usage
):
return None

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 Accept snapshots without redundant total tokens

When a valid total_token_usage snapshot contains input_tokens and output_tokens but omits the redundant total_tokens field, this check rejects the whole record even though the value is immediately recomputed below. The repository's established parser accepts this payload and derives the total (src/cost.ts:320-345), and existing session fixtures use that shape, so affected rollouts are marked token_record_invalid and completion publishes partial zero usage instead of the measured tokens; require only input and output here.

Useful? React with 👍 / 👎.

boundary_reached = False

with session.path.open("rb") as source:
for line_number, raw_line in enumerate(source, start=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 Bound rollout records before materializing them

When a discovered rollout contains a very large JSONL event, such as a command or tool result with oversized output, iterating the binary file this way materializes the entire record and the subsequent json.loads builds the full object even though almost all event types are irrelevant to accounting. The existing session reader explicitly enforces a 1 MiB event limit (src/cost.ts:59-61,252-261), but this completion path has no bound, so repository-influenced output can make complete-scan exhaust memory or hit its MCP timeout instead of returning usage; read incrementally with a maximum record size and mark oversized records unavailable.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector

Copy link
Copy Markdown

Security review completed. No security issues were found in this pull request.

Reviewed commit: 9e616407d6

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@mldangelo-oai

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review the exact updated head bb4ed32c1c11a1fdd1b98726fd0ab5944b8b0a4c. Accurate cached-token accounting and alternate cache-write regression coverage are preserved, and the test now merges cleanly with existing live-progress PR #261.

@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: bb4ed32c1c

ℹ️ 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 +514 to +516
if result["cachedInputTokens"] + result["cacheWriteInputTokens"] > result["inputTokens"]:
return None
result["totalTokens"] = result["inputTokens"] + result["outputTokens"]

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 Reject reasoning counts that exceed output

When a rollout reports reasoning_output_tokens > output_tokens, this snapshot is accepted and can be published with coverage: "complete" even though the counts are internally impossible under this repository's token model. The established parser rejects exactly this condition in sdk/typescript/src/cost.ts:328-336; apply the same check here so malformed provider metadata produces partial coverage rather than trusted usage.

Useful? React with 👍 / 👎.

Comment on lines +433 to +435
delta = {
key: value - previous[key] if value >= previous[key] else value
for key, value in snapshot.items()

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 Apply cumulative counter resets to the whole snapshot

When cumulative usage resets and the new combined total is lower but one component remains above its previous value, this per-field reset detection mixes epochs. For example, moving from input/output 10/100 to 15/20 produces deltas 5/20/35, so input is underreported and totalTokens no longer equals input plus output while coverage can remain complete. Detect a reset from the combined snapshot and then treat every current field as fresh.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector

Copy link
Copy Markdown

Security review completed. No security issues were found in this pull request.

Reviewed commit: bb4ed32c1c

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

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