Skip to content

fix(compare): normalize JSON output before validation - #704

Merged
steipete merged 1 commit into
mainfrom
codex/audit-compare-alias
Sep 22, 2026
Merged

steipete merged 1 commit into
mainfrom
codex/audit-compare-alias

Conversation

@steipete

@steipete steipete commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Compare applied --json after strict-output validation, so a valid --json-strict compare … --json invocation failed with “requires --format json” before starting an agent. Early configuration and prompt-input errors also ignored command-local JSON selection.

Resolve the alias into the shared format field before validation and remove compare's late override. Reuse its value-aware argument scan for early error formatting. The alias still forces JSON; without it, explicit root format takes precedence over local format, then configuration. Consumed option values and prompt text after -- remain literal. Disabled compare keeps its existing RUNTIME/exit-1 identity. Executed comparisons remain arrays, including when an agent fails; shared invocation errors remain JSON-RPC objects.

Validation

  • Baseline: eight of eleven selected regression/control tests fail. Candidate: all 64 flag/compare tests pass; two affected fixture tests also pass after a required-braces lint correction.
  • Final isolated P0–P2 review: no actionable findings, with complete relevant owners and tests supplied.
  • Full code/docs gate: pnpm run check and pnpm run check:docs pass on AWS Crabbox: 2,218 tests passed, four platform skips, and 180 declared coverage tests passed. Run run_e6f6d2350cafb8a17feffaf25cab2247, lease cbx_83e04c39eda3.
  • Native macOS/Node 26.8.2: independent assessment passes all 18 cases; the baseline passes nine and fails nine. Each command uses a private HOME/workspace and the actual frozen built CLI. Real finite fast and error ACP peers exchange initialize/new/prompt messages; the error peer returns a JSON-RPC prompt error rather than exiting to simulate failure.

The mixed-result command, with only local executable/path prefixes abbreviated, was:

node --import <capture-gate.mjs> <frozen-package>/dist/cli.js \
  --cwd <synthetic-workspace> --deny-all --timeout 8 --prompt-retries 0 --ttl 1 \
  --json-strict compare fast error --json SYNTHETIC_COMPARE_ALIAS

Its baseline exits 2 without admitting a peer:

{"jsonrpc":"2.0","id":null,"error":{"code":-32602,"message":"--json-strict requires --format json","data":{"acpxCode":"USAGE","origin":"cli","sessionId":"unknown"}}}

The candidate exits 1 with empty stderr and the actual complete comparison array:

[{"agent":"fast","status":"ok","stop_reason":"end_turn","wall_ms":1287,"input_tokens":null,"output_tokens":null,"total_tokens":null,"final_message":"COMPARE_ALIAS:fast","error":null,"permission_requests":0,"permission_denied":0},{"agent":"error","status":"error","stop_reason":null,"wall_ms":1400,"input_tokens":null,"output_tokens":null,"total_tokens":null,"final_message":"","error":"SYNTHETIC_COMPARE_ALIAS_FAILURE","permission_requests":0,"permission_denied":0}]

Actual request and completed error response from the admitted error peer:

{"jsonrpc":"2.0","id":2,"method":"session/prompt","params":{"sessionId":"synthetic-compare-error-31e04367-180c-4074-a211-eddb450d1d10","prompt":[{"type":"text","text":"SYNTHETIC_COMPARE_ALIAS"}]}}
{"jsonrpc":"2.0","id":2,"error":{"code":-32603,"message":"SYNTHETIC_COMPARE_ALIAS_FAILURE"}}

The 18-case set checks both the repair and its boundaries:

Cases Before Candidate
Strict alias success and mixed error (2) Premature format rejection, exit 2, no peers Real summary arrays, exits 0/1
Config, missing-file and disabled errors with alias/long form (6) Text stderr despite requested JSON JSON-RPC RUNTIME/-32603, exit 1, empty stderr, no peers
Strict plus verbose with alias (1) Wrong “requires --format json” diagnostic Preserved strict/verbose USAGE/-32602 rejection, exit 2
Long-form execution, format precedence and consumed-value/delimiter controls (9) Correct Preserved

For example, disabled compare still emits “compare subcommand is disabled by configuration (disableExec: true)” and takes precedence over a missing prompt file. A literal --json consumed by --file or root --system-prompt, or placed after the prompt delimiter, does not select JSON.

All 36 CLI captures complete with unchanged runtime fingerprints. Before: seven admitted peers, 21 typed RPC pairs and 27 completed writes. Candidate: ten peers, 30 pairs and 38 writes. CLI exits are directly observed; all 53 admitted CLI/peer identities retire without observer rescue. Peer kernel exit codes/signals remain unknown. Expected peer counts and output are assessed separately from capture integrity, so the baseline's early no-peer failures remain valid evidence.

Capture instrumentation pauses CLI/peer startup for native identity admission. It retains the existing stdin-iterator invocation observer; every case starts with closed stdin, and no payload is injected. The operator separately verified source/package provenance and the allowlisted child environment. All 529 source hashes and 38 package hashes match after commit hooks. Three intermediate CLI lookups raced their exits (one baseline and two candidate cases); initial admission, the owned OS wait and final retirement remained complete. Brief ps/exited-Node displays are retained as non-endpoint observations, without descendant claims. This bounded proof covers local output selection and shared error handling, not arbitrary Commander parser exits, permission accounting, general stdin behavior, or provider behavior.

@steipete
steipete requested a review from a team as a code owner September 22, 2026 00:18
@clawsweeper

clawsweeper Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@steipete steipete self-assigned this Sep 22, 2026
@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Sep 22, 2026
@clawsweeper

clawsweeper Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed September 21, 2026, 8:28 PM ET / September 22, 2026, 00:28 UTC.

ClawSweeper review

What this changes

Normalize compare’s JSON alias before validation and honor command-local output selection for early errors, with regression tests and documentation.

Merge readiness

✅ Ready for maintainer review

This remains a useful, well-supported fix: pinned main and v0.18.0 still exhibit the underlying defect. No actionable correctness or security findings remain.

Priority: P2
Reviewed head: b7bb6cd3ac93c60a8eedff3015a9a7c34ce2fecd

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused, sound repair with strong native before/after proof and compatibility regression coverage.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The captured PR body exercises the changed CLI validation and diagnostic paths through a built CLI on macOS/Node 26.8.2 with real ACP transport to finite peers; before/after output demonstrates strict-alias recovery, mixed-result arrays, and correctly formatted early errors.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The captured PR body exercises the changed CLI validation and diagnostic paths through a built CLI on macOS/Node 26.8.2 with real ACP transport to finite peers; before/after output demonstrates strict-alias recovery, mixed-result arrays, and correctly formatted early errors.
Evidence reviewed 8 items Introduced change verified: Reviewed the pinned base-to-head diff across all eight files; changes are limited to output selection, regression coverage, documentation, and the changelog.
Current-main defect: Main calls resolveGlobalFlags, including strict-format validation, before applying flags.json in compare’s output policy. Consequently the existing alias cannot satisfy strict validation.
Latest release also lacks normalization: The v0.18.0 source resolves format without considering opts.json and immediately validates strict compatibility; its compare handler also applies the alias afterward.
Findings None None.
Security None None.

How this fits together

The compare command sends one prompt to multiple ACP agents and summarizes their results. CLI output selection controls both those summaries and diagnostics produced before agents start.

flowchart TD
  A[CLI arguments and configuration] --> B[Resolve output selection]
  B --> C[Validate invocation]
  C -->|Invalid| D[Formatted diagnostic]
  C -->|Valid| E[Run ACP agents]
  E --> F[Comparison summary]
Loading

Before merge

None.

Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production versus test growth Production +33 net lines; tests +201 net lines The production growth supports early output selection and is accompanied by focused regression and compatibility coverage.

Technical review

Best possible solution:

Use the shared format resolver and existing argument scanner so compare summaries and early diagnostics consistently honor the established JSON alias.

Do we have a high-confidence way to reproduce the issue?

Yes: current-main source rejects --json-strict compare … --json before applying the alias, matching the supplied native baseline trace; this review did not execute it.

Is this the best way to solve the issue?

Yes: normalizing the existing alias before shared validation is a focused repair, and reusing the existing scanner preserves option-value and delimiter boundaries.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 29de86019e82.

Labels

Label changes:

  • add P2: This repairs a bounded compare CLI output-contract defect affecting strict JSON invocations and machine-readable errors.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The captured PR body exercises the changed CLI validation and diagnostic paths through a built CLI on macOS/Node 26.8.2 with real ACP transport to finite peers; before/after output demonstrates strict-alias recovery, mixed-result arrays, and correctly formatted early errors.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The captured PR body exercises the changed CLI validation and diagnostic paths through a built CLI on macOS/Node 26.8.2 with real ACP transport to finite peers; before/after output demonstrates strict-alias recovery, mixed-result arrays, and correctly formatted early errors.

Label justifications:

  • P2: This repairs a bounded compare CLI output-contract defect affecting strict JSON invocations and machine-readable errors.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The captured PR body exercises the changed CLI validation and diagnostic paths through a built CLI on macOS/Node 26.8.2 with real ACP transport to finite peers; before/after output demonstrates strict-alias recovery, mixed-result arrays, and correctly formatted early errors.
  • proof: sufficient: Contributor real behavior proof is sufficient. The captured PR body exercises the changed CLI validation and diagnostic paths through a built CLI on macOS/Node 26.8.2 with real ACP transport to finite peers; before/after output demonstrates strict-alias recovery, mixed-result arrays, and correctly formatted early errors.

Evidence

What I checked:

  • Introduced change verified: Reviewed the pinned base-to-head diff across all eight files; changes are limited to output selection, regression coverage, documentation, and the changelog. (b7bb6cd3ac93)
  • Current-main defect: Main calls resolveGlobalFlags, including strict-format validation, before applying flags.json in compare’s output policy. Consequently the existing alias cannot satisfy strict validation. (src/cli/compare-command.ts:378, 29de86019e82)
  • Latest release also lacks normalization: The v0.18.0 source resolves format without considering opts.json and immediately validates strict compatibility; its compare handler also applies the alias afterward. (src/cli/flags.ts:440, 8699be1b6428)
  • Regression and compatibility coverage: Added cases cover strict alias success, mixed agent results, root/local/config precedence, early configuration and prompt-file errors, strict/verbose rejection, consumed values, and delimiter literals. (test/compare-command.test.ts:431, b7bb6cd3ac93)
  • Real production-path proof: The fully supplied PR body at captured sourceRevision 0a065a731c7f1298896389b05434795fe1cbac2357a7a4d34fb5f9c76d756d8b contains native macOS/Node 26.8.2 built-CLI before/after output. Strict alias execution changes from premature exit 2 to an actual comparison array, including a real ACP session/prompt error response, exit 1, and empty stderr. The 18-case matrix also records early errors and compatibility controls. Reported code/docs checks and supplied successful CI are supplemental; this reviewer did not execute target code. (b7bb6cd3ac93)
  • Relevant merged history: The argument scanner comes from merged fix: preserve compare input and execution boundaries #634. Raw commit parentage and its parent-to-commit patch confirm the scanner’s source-line addition; the earlier change addresses input boundaries, workspace selection, and interruption rather than this JSON validation defect. (src/cli/compare-args.ts:25, 3e865eb5c909)

Likely related people:

  • Peter Steinberger: Raw commit 3e865eb adds src/cli/compare-args.ts:25 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 3e865eb5c909; files: src/cli/compare-args.ts)
  • mvanhorn: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@steipete
steipete merged commit 8d29425 into main Sep 22, 2026
22 checks passed
@steipete
steipete deleted the codex/audit-compare-alias branch September 22, 2026 00:30
@steipete

Copy link
Copy Markdown
Contributor Author

Landed as 8d29425 from reviewed head b7bb6cd. The merged tree matches the reviewed/native-tested tree; main is clean and synchronized.

Compare now resolves its JSON alias before validation and selects matching diagnostics for early configuration/input failures. The existing value-aware scan keeps literal option values and prompt delimiters intact. Comparison arrays, shared JSON-RPC errors, disabled-comparison classification, and format precedence retain their established roles.

Validation passed: 64 focused tests, the full code/docs gate (2,218 tests plus 180 declared coverage tests), independent native assessment (9/18 before, 18/18 after), isolated review, all 12 CI jobs, and CodeQL. The first remote gate caught a missing brace in a test fixture; that was corrected before the successful final gate/review, with production package bytes unchanged. Public review found no actionable defects.

Both AWS leases are released with broker cleanup complete. Native fixture archives were hash-verified before removing the exact scratch directories. All raw protocol, API-output and process receipts remain retained, including the three resolved intermediate CLI lookup races and the stated unknown peer kernel exit statuses.

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

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant