Skip to content

fix: report prompt size when synthesis fails - #243

Merged
kunchenguid merged 3 commits into
kunchenguid:mainfrom
WTF-Am-ID:fm/up-backpass-76
Oct 28, 2026
Merged

kunchenguid merged 3 commits into
kunchenguid:mainfrom
WTF-Am-ID:fm/up-backpass-76

Conversation

@WTF-Am-ID

@WTF-Am-ID WTF-Am-ID commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Intent

1 p1-p7 codex only no cross model review

Context: this task is upstream scan 3 pick P6: upstream issue #76 in kunchenguid/backpass. The owner approved fixing scan picks P1-P7 upstream through PRs, written and reviewed by Codex only, with no cross-model review. Scan notes for this pick:

Issue: #76

A failed synthesis call does not report the assembled prompt's measured size, hiding an oversized-input diagnosis.
Size S.
Contract class: restore.
Recorded triage.

Current-source evidence: Pinned Backpass src/synthesize.js:647-648 writes the assembled edit prompt, and :733-744 rethrows prompt failure after closing the session. The inspected path adds no measured byte/token-size diagnostic.

Competing-work check: No exact target among all 18 proposal bodies/titles. File inventories and the synthesize/acpx diffs were checked for the model-catalog and ambiguous-anchor proposals. They resolve model families or reject individual edits, not failure-time prompt-size reporting. The analysis failure-trace proposal concerns a different phase.

Closure test: With a fake ACP adapter force synthesis failure for both a small prompt and an oversized synthetic prompt. The error must name the synthesis phase, measured UTF-8 bytes and a clearly labelled token estimate if supplied, without printing prompt contents. Check a non-ASCII prompt so bytes and characters are not confused. Keep the original error class/cause and success behavior. Do not infer that size caused every failure or introduce an unrequested hard ceiling.

What Changed

Review was single-family (Codex) by the owner's temporary choice.

Risk Assessment

✅ Low: The bounded change preserves measured prompt bytes through failure reporting without changing successful execution or adding prompt disclosure or size limits.

Testing

Focused tests passed, and the regression failed against baseline on the missing diagnostic. Live Codex/acpx fault probes verified byte counts, pinned-error propagation, annotation sizing and unchanged empty-proposal behavior. CLI transcripts were retained; no provider inference, full suite, lint or static analysis ran. Disposable data was removed.

  • Live validation: ✅ go - 5 of 5 scenarios driven live against the product
Scenario Result Live Evidence
A pinned Codex synthesis process fails on a small non-ASCII prompt and reports exactly 7,826 UTF-8 bytes without exposing prompt contents. ✅ pass live pinned-small-cli.log; verification.md
An oversized non-ASCII prompt reaches Codex without a new size ceiling, and process failure preserves the 1,268,494-byte diagnostic through the pinned wrapper. ✅ pass live pinned-large-cli.log; verification.md
Real acpx timeouts retain timeout classification and report the failed synthesis prompt’s measured size. ✅ pass live small-cli.log; large-cli.log; timeout-results.json
An annotation timeout reports its own 4,217-byte prompt rather than the earlier edit prompt. ✅ pass live annotation-cli.log
Codex adapter empty turns produce the same empty proposal and budget as baseline, without adding a failure diagnostic. ✅ pass live empty-success-cli.log; baseline-empty-success-cli.log; verification.md
Evidence: Verification report and live CLI transcripts
# Synthesis failure diagnostics verification

Candidate: 8f15102c91efb4ae5cbf52c25621db51bc3d1463
Baseline: bc7bc6458cd2a023606d826a195decd3d72b7016

Executed the real `node bin/backpass.js propose --synthesis-agent codex` CLI in disposable repositories. HOME, CODEX_HOME, XDG_CONFIG_HOME, TMPDIR, npm cache, acpx config, transcript stores and analysis evidence were isolated beneath `.validation-76/` in the worktree. Codex CLI 0.165.0 and the installed acpx were used. No real credentials or operator transcripts were used. Dummy API authentication pointed the real Codex ACP adapter at a loopback-only HTTP server. No provider model inference was performed or billed.

Fault injection used a pass-through launcher for the installed acpx. For pinned failures it terminated the actual acpx prompt process with SIGTERM after 2.5 seconds. For timeout failures it passed a two-second deadline to the actual acpx prompt. It fabricated neither ACP replies nor acpx exit statuses. Annotation setup allowed one HTTP 400 empty turn before holding the next request open. The successful empty-proposal comparison used HTTP 400 empty turns throughout, so it verifies the existing no-edit success path, not model edit quality.

Focused checks: `node --import ./test/helpers/hermetic-config.js --test test/synthesize.test.js test/synthesis-cli.test.js` passed. These include synthetic adapters for edit success, error classes/fields/cause, session closure and small/oversized non-ASCII failure regression. Those synthetic checks are not live evidence. No full suite, lint, formatter or static analysis ran.

Baseline regression: candidate regression test run against a disposable source copy with baseline `src/agents.js` and `src/synthesize.js` fails because `synthesis prompt size: 7483 UTF-8 bytes` is missing. The first attempt encountered a Node 26 temporary-script module-type setup problem, fixed by isolating the temporary root with `type: commonjs`; the retained baseline log is the corrected run. Initial live setup also required selecting an advertised model and dummy acpx authentication. A connection-refusal attempt retried instead of terminating and was cancelled and closed. HTTP 400 alone produced empty successful turns, motivating the process/deadline fault injection.

Each failed live scenario exited 1, retained the independently measured byte count, excluded the private prompt marker, and left the original memory file unchanged. Byte counts were computed by reading the generated prompt artifact as UTF-8 and comparing Buffer.byteLength with the displayed value. No token estimate was supplied, and no failure was attributed to size.

## pinned-small

Prompt bytes: 7826; UTF-16 code units: 7814; exit: 1; private marker leaked: False; original memory unchanged: True.

`` `text
· synthesizing with codex effort=high
error pinned synthesis agent codex failed unexpectedly (acpx codex session prompt failed (exit null): exit null
Synthesis prompt size: 7826 UTF-8 bytes.)
  check the codex/acpx failure above and retry; or pin a different harness: backpass --synthesis-agent <agent>
`` `

## pinned-large

Prompt bytes: 1268494; UTF-16 code units: 548486; exit: 1; private marker leaked: False; original memory unchanged: True.

`` `text
· synthesizing with codex effort=high
error pinned synthesis agent codex failed unexpectedly (acpx codex session prompt failed (exit null): exit null
Synthesis prompt size: 1268494 UTF-8 bytes.)
  check the codex/acpx failure above and retry; or pin a different harness: backpass --synthesis-agent <agent>
`` `

## annotation

Prompt bytes: 4217; UTF-16 code units: 4215; exit: 1; private marker leaked: False; original memory unchanged: True.

`` `text
· synthesizing with codex effort=high
error acpx codex session prompt ended at its own timeout (exit 3): [acpx] error: TIMEOUT QUEUE_RUNTIME_PROMPT_FAILED Timed out after 2000ms
Synthesis prompt size: 4217 UTF-8 bytes.
  the harness call timed out; rerun to retry - finished analysis is cached - or raise timeoutSeconds in the backpass config
`` `

## empty-success

Prompt bytes: 7826; UTF-16 code units: 7814; exit: 0; private marker leaked: False; original memory unchanged: True.

`` `text

proposal · backpass-cli-5bex6M · AGENTS.md · 0 edit(s) from 3 session(s) · interactive 3 · non-interactive 0
  budget [................................] 10 -> 10 / 5,000 tok
  evidence: 0 positive · 3 negative · 0 gap clusters

  no edits proposed - the evidence did not clear the thresholds this run

  tier-2 tokens: not reported by codex

Review and apply with `backpass apply` (nothing has been written).
· synthesizing with codex effort=high
`` `

## small

Prompt bytes: 7826; UTF-16 code units: 7814; exit: 1; private marker leaked: False; original memory unchanged: True.

`` `text
· synthesizing with codex effort=high
error acpx codex session prompt ended at its own timeout (exit 3): [acpx] error: TIMEOUT QUEUE_RUNTIME_PROMPT_FAILED Timed out after 2000ms
Synthesis prompt size: 7826 UTF-8 bytes.
  the harness call timed out; rerun to retry - finished analysis is cached - or raise timeoutSeconds in the backpass config
`` `

## large

Prompt bytes: 1268494; UTF-16 code units: 548486; exit: 1; private marker leaked: False; original memory unchanged: True.

`` `text
· synthesizing with codex effort=high
error acpx codex session prompt ended at its own timeout (exit 3): [acpx] error: TIMEOUT QUEUE_RUNTIME_PROMPT_FAILED Timed out after 2000ms
Synthesis prompt size: 1268494 UTF-8 bytes.
  the harness call timed out; rerun to retry - finished analysis is cached - or raise timeoutSeconds in the backpass config
`` `

Baseline and candidate empty-proposal semantics match: zero edits, unchanged 10-token current/projected budget, no notes or violations. Success logs contain no added failure diagnostic. Focused adapter regressions additionally exercise successful staging edits and proposal/application behavior.
Evidence: Oversized pinned synthesis failure
$ node bin/backpass.js propose --synthesis-agent codex
· synthesizing with codex effort=high
error pinned synthesis agent codex failed unexpectedly (acpx codex session prompt failed (exit null): exit null
Synthesis prompt size: 1268494 UTF-8 bytes.)
  check the codex/acpx failure above and retry; or pin a different harness: backpass --synthesis-agent <agent>

Exit status: 1
Measured assembled prompt: 1268494 UTF-8 bytes, 548486 JavaScript UTF-16 code units.
Evidence: Annotation failure diagnostic
$ node bin/backpass.js propose --synthesis-agent codex
· synthesizing with codex effort=high
error acpx codex session prompt ended at its own timeout (exit 3): [acpx] error: TIMEOUT QUEUE_RUNTIME_PROMPT_FAILED Timed out after 2000ms
Synthesis prompt size: 4217 UTF-8 bytes.
  the harness call timed out; rerun to retry - finished analysis is cached - or raise timeoutSeconds in the backpass config

Exit status: 1
Measured assembled prompt: 4217 UTF-8 bytes, 4215 JavaScript UTF-16 code units.
Evidence: Baseline regression reproduction
$ node --import ./test/helpers/hermetic-config.js --test --test-name-pattern="failed synthesis reports" test/synthesize.test.js
Baseline source: bc7bc6458cd2a023606d826a195decd3d72b7016, candidate regression test
✖ failed synthesis reports the failed prompt's UTF-8 bytes without its contents (502.993208ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 595.425084

✖ failing tests:

test at test/synthesize.test.js:215:1
✖ failed synthesis reports the failed prompt's UTF-8 bytes without its contents (502.993208ms)
  AssertionError [ERR_ASSERTION]: The input did not match the regular expression /synthesis prompt size: 7483 UTF-8 bytes/i. Input:
  
  'pinned synthesis agent codex failed unexpectedly (acpx codex session prompt failed (exit 1): synthetic adapter failure)'
  
      at Object.<anonymous> (file://~/.no-mistakes-upstream-codex/worktrees/8c18a660049c/01M619K73SR1R9CTG477856YRY/.validation-76/baseline-product/test/synthesize.test.js:248:14)
      at expectedException (node:assert:547:17)
      at expectsError (node:assert:672:21)
      at strict.rejects (node:assert:738:3)
      at async TestContext.<anonymous> (file://~/.no-mistakes-upstream-codex/worktrees/8c18a660049c/01M619K73SR1R9CTG477856YRY/.validation-76/baseline-product/test/synthesize.test.js:230:5)
      at async Test.run (node:internal/test_runner/test:1409:7)
      at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
    generatedMessage: true,
    code: 'ERR_ASSERTION',
    actual: 'pinned synthesis agent codex failed unexpectedly (acpx codex session prompt failed (exit 1): synthetic adapter failure)',
    expected: /synthesis prompt size: 7483 UTF-8 bytes/i,
    operator: 'match',
    diff: 'simple'
  }

Exit status: 1

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed ✅
  • 🚨 src/synthesize.js:715 - Pinned synthesis failures still hide the size diagnostic. With --synthesis-agent codex, an edit prompt exiting 1 reaches the new \nSynthesis prompt size: ... suffix here through src/synthesize.js:742. AgentResolver.withFallthrough then wraps it at src/agents.js:525, and pinnedFailureError discards everything after the first newline at src/agents.js:570. CLI output therefore omits bytes for both small and oversized prompts, contradicting the required criterion: "The error must name the synthesis phase, measured UTF-8 bytes". The replacement resolver in test/synthesize.test.js:229 bypasses this production behavior. Preserve the diagnostic across the resolver's error boundary and cover the real pinned path.

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 5 of 5 scenarios driven live against the product
Scenario Result Live Evidence
A pinned Codex synthesis process fails on a small non-ASCII prompt and reports exactly 7,826 UTF-8 bytes without exposing prompt contents. ✅ pass live pinned-small-cli.log; verification.md
An oversized non-ASCII prompt reaches Codex without a new size ceiling, and process failure preserves the 1,268,494-byte diagnostic through the pinned wrapper. ✅ pass live pinned-large-cli.log; verification.md
Real acpx timeouts retain timeout classification and report the failed synthesis prompt’s measured size. ✅ pass live small-cli.log; large-cli.log; timeout-results.json
An annotation timeout reports its own 4,217-byte prompt rather than the earlier edit prompt. ✅ pass live annotation-cli.log
Codex adapter empty turns produce the same empty proposal and budget as baseline, without adding a failure diagnostic. ✅ pass live empty-success-cli.log; baseline-empty-success-cli.log; verification.md
  • node --import ./test/helpers/hermetic-config.js --test test/synthesize.test.js test/synthesis-cli.test.js
  • node .validation-76/probe.mjs with isolated Codex/acpx configuration and dummy authentication
  • node .validation-76/drive.mjs for real CLI process failures, timeouts, annotation failure and empty-proposal behavior
  • node .validation-76/drive-baseline.mjs and comparison of baseline/candidate proposal fields
  • node --import ./test/helpers/hermetic-config.js --test --test-name-pattern=&#34;failed synthesis reports&#34; test/synthesize.test.js against disposable baseline source
  • Closed disposable acpx sessions, removed .validation-76, and confirmed a clean worktree at the target revision
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@greptile-apps

greptile-apps Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Low impact] The PR appears safe to merge.

Reviews (1) · Last reviewed commit: "no-mistakes(document): Document synthesi..." · Reviewed by Greptile

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate.

I reviewed the diff (no workflow, network, or exec changes) and approved fork CI. Everything is green, including no-mistakes, and the PR is CLEAN. It closes #76: a failed synthesis prompt (edit or annotate) now reports the measured UTF-8 byte size of the prompt file it sent, without printing the prompt and without claiming size was the cause, and pinned-agent failures keep that line instead of cutting the message to its first line. I checked that every AcpxError message already takes only the first stderr line, so the wider pinned message doesn't start dumping harness output. Live evidence covers 5 of 5 scenarios, including an unchanged empty-proposal path.

VISION per-rule:

  • Evidence is the only currency: not affected.
  • The human owns the weights: aligns (no write-gate change).
  • Nothing the model says is taken on faith: aligns (the size is measured from the prompt artifact, not taken from the model).
  • The budget is the constraint: aligns (no new ceiling or refusal).
  • It reads what you already have and owns nothing: aligns (prompt contents are not printed).
  • Failure is loud and named: aligns (a prompt-size death is no longer indistinguishable from an auth or harness failure).
  • Scope: aligns.

Contract-class: restore. The failure path is already promised to name its condition; this adds the measured size to an existing failure message and leaves the successful path and the agent ladder unchanged. Merging.

@kunchenguid
kunchenguid merged commit a2d710b into kunchenguid:main Oct 28, 2026
6 checks passed
@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: this is merged. Thank you @WTF-Am-ID — really appreciate you taking the time on this.

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.

propose failure surfaces as bare AcpxError exit 1 — no prompt-size hint when the synthesis prompt is too large

2 participants