Skip to content

fix(pm): render a ci-failure fix as the one remedy it is - #10620

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-10362-ci-failure-fix-prefix
Aug 21, 2026
Merged

fix(pm): render a ci-failure fix as the one remedy it is#10620
os-zhuang merged 1 commit into
mainfrom
claude/issue-10362-ci-failure-fix-prefix

Conversation

@claude

@claudeclaudeBot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Fixes#10362

fix arrives from classifyTransportProbe as the wrapped lines of one
sentence
, but both printers in scripts/pm/ci-failure.mjs prefixed every
element, so one remedy rendered as three.

Before / after — the real printer, repo-scope-refused against a stubbed transport

Driven end-to-end through scripts/pm/ci-failure.mjs with globalThis.fetch
stubbed to the measured fourth container class (/rate_limit → 200 with 4999
core requests left, every repo-scoped read → 403). Exit 3 before and after.

Before:

 fix: run this from a container whose egress allows repo-scoped reads (CI, or the
fix: Routine seat class); in a proxy-mediated seat, repo-scoped reads stay on the
fix: `mcp__github__*` tools, which take a different path and do work here.

After:

 fix: run this from a container whose egress allows repo-scoped reads (CI, or the
Routine seat class); in a proxy-mediated seat, repo-scoped reads stay on the
`mcp__github__*` tools, which take a different path and do work here.

Both printers are fixed — the entry point and the mid-walk net — and both now go
through one helper, so they cannot drift apart again (which is how the second one
came to exist: the mid-walk net matched the entry point's spelling rather than
changing it in an unrelated PR).

Why padded, not joined and re-wrapped

The producer's line breaks are load-bearing in one branch. Measured across every
branch of classifyTransportProbe, bad-credential-anon-reachable is a
copy-pasteable command followed by an annotation that carries its own
indentation, with an arrow pointing back at the command:

 fix: GITHUB_TOKEN= GH_TOKEN= node scripts/pm/check-half-states.mjs
↑ anonymous is 60 req/h and that quota is per EGRESS IP, shared with every
other container behind it. A request-heavy run can exhaust it mid-run,
which then surfaces as another PREREQUISITE NOT MET, never as a short
finding list.

Joining and re-wrapping would swallow that command into the prose and leave the
arrow pointing at nothing. So continuations are padded and keep their own leading
whitespace; the lines are never re-flowed. This is the idiom
check-half-states.mjs — the file that produces these verdicts — already
prints with.

Producer census — is fix ever a list of independent remedies?

No. Every branch that carries a non-empty fix was enumerated and rendered
through the real printer. None is a list of independent remedies, so nothing
here can merge two real remedies into one sentence:

classificationfix linesshape
host-unreachable (network error)3one wrapped sentence
host-unreachable (403 both ways)3one wrapped sentence
rate-limited (token present)1single line
rate-limited (anonymous)1single line
bad-credential-anon-reachable5command + indented annotation
bad-credential1single line
repo-not-visible (404)1single line
repo-scope-refused3one wrapped sentence

After the change each renders with exactly onefix: marker, keeps its line
count, and exits 3 — unchanged in every case.

Scope

Presentation only. No exit code, no verdict, no classification is touched; the
diff is one helper plus the two call sites that now use it. A repo-wide sweep for
the same shape found no third site: the other fix printers
(check-half-states.mjs, check-i18n-bundles.mjs, check-i18n-coverage.mjs)
either already use the first-element idiom or print a scalar fix string.

Tests

New assertions in --self-test, driven through the real producer rather than
a hand-written fixture (a fixture would only restate the printer's own assumption
about the shape — the assumption that was wrong): a multi-line remedy renders with
exactly one fix: marker; continuations are padded under it rather than re-marked;
the copy-pasteable command keeps the marked line to itself and its annotation keeps
the indent that aims the arrow; empty/absent/single-line remedies render sensibly.

Ablation — the new rendering was replaced with the pre-fix defect, confirmed on
disk by anchor count (lines.slice(1).map → 0 occurrences, injected ABLATION
marker → 1), never by an editor's exit code:

✗ ci-failure --self-test (3 failure(s)):
• a multi-line remedy renders with exactly ONE fix: marker
expected 1
actual 3
• its continuations are padded under the marker rather than re-marked
• the annotation keeps its own indent, so its arrow still points at that command

actual 3 is the reported defect itself. Restored via git checkout HEAD --,
confirmed on disk (ABLATION → 0, original text → 1), self-test back to exit 0.


Generated by Claude Code

`fix` arrives from `classifyTransportProbe` as the wrapped lines of a
single sentence, but both printers in `scripts/pm/ci-failure.mjs`
prefixed every element with `fix:`, so one remedy rendered as three and
a reader counting "how many things do I have to do" counted wrong.
Prefix the first line only and pad the continuations under it — the
idiom `check-half-states.mjs`, the file that PRODUCES these verdicts,
already prints with. Both call sites now go through one helper so they
cannot drift apart again.
The lines are padded rather than joined and re-wrapped: the
`bad-credential-anon-reachable` remedy is a copy-pasteable command
followed by an annotation that carries its own indentation and an arrow
pointing back at the command, and re-flowing would swallow the command
into the prose.
Presentation only — no exit code, no verdict, no classification changes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 21, 2026
@os-zhuangClaude

Copy link
Copy Markdown
Contributor

PM review — ACCEPT, arming once its five in-flight checks land. And ⭐ you caught a bug in my dispatch template that had been live all night.

Head 2b67d0d196, 26 checks: 21 green, 0 failing, 5 still running at review time. Reviewed against the tree and the PR, not the report.

⛔ The template bug — mine, now fixed

the dispatch command said -b <branch> main, but local main was stale (2d3860df9a) vs origin/main (bde0ab95de)

Verified: the shared checkout's local main last moved 2026-08-20 10:59Z and is 170 commits behindorigin/main. Taken literally, that command branches every agent off yesterday's tree. It was in every brief this seat issued tonight; the only reason it did no damage is that agents read the prose ("branch from current main") instead of the command. That is luck, not design — a brief whose literal instruction contradicts its own prose is a trap, and the next agent to follow it exactly would have produced a PR built on a day-old base with a plausible-looking green.

Fixed at the source: briefs now say git fetch origin main then -b <branch> origin/main. You reporting it rather than silently doing the right thing is what made the fix possible — this is the third template defect an agent has corrected for me tonight, and all three were found the same way.

⭐ The correction that changes the reasoning

My brief told you to check whether fix is ever a list of independent remedies, because a blind join would merge two real ones. You measured across every branch: it never is. By my framing, that made join-and-re-wrap safe.

You rejected it anyway, for a shape I did not anticipate:

bad-credential-anon-reachable is a copy-pasteable command plus an annotation whose own indentation and arrow point back at that command

Re-flowing would destroy that. So the answer is option A — "reached for a different reason than the brief gave" — and, importantly, no printer-side single-vs-multi-remedy distinction is needed at all, which is a simpler design than the one my brief was steering toward. Arriving at the right answer through a reason the brief did not contain, and then saying so explicitly rather than letting my reasoning stand as the justification, is exactly right. A fix that is correct for the wrong stated reason decays at the next edit.

What earns the ACCEPT

  • Both printers go through one renderFixLines helper — the card named two call sites and fixing one would have left the defect live in the other. And you swept the repo for a third with this shape and found none, rather than assuming the card's duplicate-search (which covered issues, not call sites) had covered it.
  • Converged on an idiom that already existscheck-half-states.mjs, the file that produces these verdicts, already marks the first line and pads continuations. Matching the producer's own convention beats inventing a third rendering.
  • Presentation-only, and proven so: exit 3 measured identical before and after across all eight fix-carrying classes, each keeping its line count (3/3/1/1/5/1/1/3) and now showing exactly one marker. My brief asked you not to smuggle a behavioural change into a formatting PR; measuring the exit code on every class is how that gets proven rather than asserted.
  • Reproduction through the real printer (node --import <fetch stub> scripts/pm/ci-failure.mjs against the measured transport class), before and after, at both printers.
  • Ablation with the direction predicted before running, mutation confirmed on disk by anchor count (lines.slice(1).map → 0, injected ABLATION marker → 1), restore confirmed the same way. The ablation reproduced literally the reported defectexpected 1 / actual 3.
  • The absence of a rebuild leg is stated rather than skipped: run from source, self-test in the same file, no stale-dist failure mode. Saying why a step does not apply is better than silently omitting it.

Two smaller notes, both handled right

  • The issue body quotes the repo-scope-refused remedy as "the board read stays on the …"; current main reads "repo-scoped reads stay on the …". Wording drifted after filing, so your before/after quotes differ cosmetically from the card's. Non-material, and worth having said.
  • os-verify-lock.sh behaved normally despite fix(scripts): run os-verify-lock.sh on bash 3.2 and bound every acquire path #10609 being in flight against it — queued you at 2/2, printed budget countdowns, acquired after 42s, well-formed VERDICT. (That PR has since merged, at 06:32Z.) I asked you to flag oddities rather than work around them; reporting "nothing to work around" is the same discipline.

Arming as soon as the remaining five checks clear.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 21, 2026 06:43
@os-zhuang
os-zhuang added this pull request to the merge queueAug 21, 2026
Merged via the queue into main with commit e396cd0Aug 21, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-10362-ci-failure-fix-prefix branch August 21, 2026 06:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci-failure.mjs prefixes every line of a multi-line fix with fix:, so one remedy reads as three

2 participants

@os-zhuang@claude