Skip to content

feat(plugin): prototype TypeScript candidate normalization - #643

Open
mldangelo-oai wants to merge 1 commit into
mainfrom
mdangelo/codex/prototype-normalize-candidates-ts
Open

feat(plugin): prototype TypeScript candidate normalization#643
mldangelo-oai wants to merge 1 commit into
mainfrom
mdangelo/codex/prototype-normalize-candidates-ts

Conversation

@mldangelo-oai

Copy link
Copy Markdown
Collaborator

Summary

Add a side-by-side TypeScript prototype for candidate normalization while preserving the existing Python implementation as the production entrypoint.

Changes

  • Generate and bundle a standalone JavaScript normalizer from its TypeScript source.
  • Compare Python and TypeScript behavior with deterministic, filesystem, and property-based differential tests.
  • Preserve candidate ordering, JSONL output, path containment, atomic output replacement, and existing command arguments.
  • Check generated output during type validation and keep subprocess-heavy property tests within the existing timeout.
  • Leave the production Python helper and public CLI surface unchanged.

Testing

  • pnpm run types
  • pnpm run format
  • bun test --timeout 30000 --seed 12345 tests-ts/normalize-candidates.test.ts tests-ts/normalize-candidates-filesystem.test.ts tests-ts/normalize-candidates.property.test.ts tests-ts/skeleton.test.ts: 26 passed, one Windows-only test skipped on macOS.
  • bun test --timeout 30000 --seed 12345 ./tests-ts: 1,787 passed, 31 platform-specific skips, and zero failures on the previously verified base before the final refresh from main.
  • pnpm pack --pack-destination <temporary-directory> and node scripts/check-package.mjs <archive>: 270 archive entries, installed SDK import, NodeNext types, CLI, 114 bundled plugin files, bundled Codex, and nested-worker smoke checks passed on the refreshed base.

Risk and rollout

The Python normalizer remains the production implementation. The TypeScript helper is bundled alongside it for parity testing, with no public CLI changes or production cutover. Windows-specific parity coverage will run in pull-request CI.

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.

@github-actions github-actions Bot added the enhancement New feature or request label Aug 25, 2026
@mldangelo-oai

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 25, 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-08-25T07:02:34.250849Z f2a088a Manual request
🔒 Security Review Completed 2026-08-25T06:58:55.753170Z f2a088a PR opened
ℹ️ 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.

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

ℹ️ 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 +732 to +735
if (inputs.includes(output))
throw new Error("--out: must not also be an input");
if (output === scopePath) {
throw new Error("--out: must not replace --in-scope-files");

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 Canonicalize Windows paths before overwrite checks

On Windows, path identity is case-insensitive, but these string comparisons are case-sensitive and resolveAllowMissing() preserves the caller's spelling. If an existing input or scope file is passed as --out with different drive, directory, or filename casing, the check is bypassed and the final rename replaces that protected file; Python's Path comparison rejects the same invocation. Compare canonical Windows path keys or otherwise use case-insensitive path identity here.

AGENTS.md reference: sdk/typescript/AGENTS.md:L22-L27

Useful? React with 👍 / 👎.

Comment on lines +235 to +239
function positiveLine(value: unknown, field: string): number {
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
throw new Error(`${field}: expected a positive integer`);
}
return value;

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 decimal spellings for line numbers

When model output encodes a line as 1.0 or 1e0, JSON.parse produces the number 1, so this check accepts it and writes a candidate; the Python oracle parses those tokens as floats and rejects them because they are not int values. This breaks the documented rejected-input parity during migration and can cause previously invalid candidate rows to enter scan results, so the JSON token type must be preserved or validated before coercion.

AGENTS.md reference: AGENTS.md:L21-L24

Useful? React with 👍 / 👎.

Comment on lines +781 to +785
const entrypoint = process.argv[1];
if (
entrypoint !== undefined &&
import.meta.url === pathToFileURL(resolve(entrypoint)).href
) {

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 Resolve symlinks before detecting the CLI entrypoint

When this script or any parent directory is invoked through a filesystem symlink, Node resolves import.meta.url to the real module while process.argv[1] retains the symlink spelling, so this condition is false and the process exits successfully without reading candidates or producing output. The Python helper works through the same symlink, so canonicalize both paths before comparing them to preserve executable behavior.

AGENTS.md reference: AGENTS.md:L21-L24

Useful? React with 👍 / 👎.

Comment on lines +762 to +764
async function runCli(): Promise<void> {
if (process.argv.slice(2).some(isHelpArgument)) {
console.log(HELP);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Parse help after validating preceding option values

If --help or -h follows an option whose value is missing, such as --out --help or --input --help, this pre-scan prints help and exits successfully. The Python CLI instead reports the missing value and exits with status 2, so malformed automation can be silently treated as successful during migration; process help in argument order rather than bypassing parsing whenever it appears.

AGENTS.md reference: AGENTS.md:L28-L31

Useful? React with 👍 / 👎.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant