Skip to content

test(dev-up): report failed image builds and dev-up stderr tails - #2805

Open
freeqaz wants to merge 7 commits into
mainfrom
lows-1018a-1847
Open

freeqaz wants to merge 7 commits into
mainfrom
lows-1018a-1847

Conversation

@freeqaz

@freeqaz freeqaz commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

A dev-up-k3d cell (run 39062400368, job 117483169349) failed every image-building test, and the diagnostics did not record why:

  • The provider fixture threw AggregateError("Checkout image builds failed"). The runner reports only a failure's message and stack, so the buildx reasons were lost.
  • The NetworkPolicy refusal test's first assertion failed on stdout, so its message showed stdout only. dev-up's stderr, where the cause would be, was never shown.

Before choosing a retry or other fix, we need a sighting that names the cause (internal finding 1847).

Change

  • imageBuildFailure (tests/helpers/dev-up-model-provider.mjs) names each failed checkout build (controller, runtime base) with its exit status or signal and the end of its stderr. It still keeps the rejection reasons on the AggregateError.
  • outputTail keeps the end of a command's output within a size budget. It first masks, over the whole output, every line that a multiline secret reaches: private keys (as before), the lines after a secret key's line that its value reaches (a credential object printed across lines is masked through its closing brace, even when its opening line falls outside the tail), and lines of an environment value that holds a newline. That is maskSecretLines, new in scripts/ci/failure-redaction.mjs; its spans come from the runner's own assignment rule, which also resumes after a value found on the next line so a key there (token: above credentials: {) is not lost. Then it walks back from the end, redacting each line as the runner does (redactOutputLine, unchanged) until the budget or 80 lines is reached, and marks omitted earlier output. Single-line output is redacted exactly as before. Buildx and dev-up print their progress before the cause, while the runner keeps only the first 16 KiB of a message. So the budgets (6 KB per build, 3 KB stdout plus 8 KB stderr for dev-up) keep both builds, or dev-up's cause, inside it.
  • The refusal test now puts dev-up's stderr tail, then its stdout tail, in each assertion message. Stderr comes first so the job log's short copy starts with the cause.
  • No retry and no timeout change.

Verification

  • node --test tests/integration/dev-up-model-provider.test.mjs: 18/18 pass, and 20/20 runs pinned to 2 CPUs. The new test covers one build failing and both builds failing, each with 2,000 progress lines, an env secret and a private key in stderr. It checks each image's exit status and final error line, redaction, the omission marker and the 16 KiB bound.
  • With main's helper, the new test fails (the message carries no build output).
  • A second new test feeds invented values in nine shapes: a multiline credentials object with its opener inside and before the last 80 lines, an opener on a private key line, an auth scheme after a key (token: Bearer …), a private key starting before the tail, a key below an empty key (token: above credentials: {), a value on the line after its key, and both of those next-line shapes below an empty key. It checks that the value is absent from the message, the diagnostic and the job-log copy, and that the diagnostic still ends with the build cause. The multiline and empty-key shapes leak on 9876a71.
  • ci-runner.test.mjs and k3d-failure-diagnostics.test.mjs (the other redaction users) still pass.
  • dev-up-k3d-real.test.mjs loads and skips without opt-in. Not run: the real dev-up k3d lane. CI's local-installation lane covers it.
  • eslint and prettier are clean on the touched files.
  • Open PRs touching these files (fix(runtime): copy public install helpers for Podman builds #1543 and the OpenShell ones) get no new conflicted file or hunk compared with main.

Risks

Test-only diagnostics. If either refusal assertion fails, its message is longer now. That message is redacted in the test, and the runner redacts it again. A secret value whose quote or bracket never closes now masks every later line of that output (as the reporter's whole-message redaction does within its 16 KiB), so the diagnostic loses the cause rather than risk showing a value.

A dev-up-k3d cell (run 39062400368) failed with "Checkout image builds
failed" and a refusal-test mismatch that showed only dev-up's stdout, so the
diagnostics never recorded why the builds or dev-up stopped (finding 1847).

The provider fixture now names each failed checkout build with its exit
status and the redacted end of its stderr (bounded so both builds fit the
runner's 16 KiB message budget). The NetworkPolicy refusal test puts dev-up's
stdout and stderr tails into each mismatch message. No retry or timeout
change: decide that once a sighting names the cause.
Internal review: redacting a tail one line at a time left a PEM body that
the runner's multi-line masking can no longer find once its BEGIN line is
replaced. Redact tails as the runner redacts failed-file output
(redactOutputLines), put dev-up's stderr tail first so the job log's short
copy starts with the cause, and compute the secrets once per failure.
@freeqaz
freeqaz requested a review from a team as a code owner October 18, 2026 01:13
@clawsweeper

clawsweeper Bot commented Oct 18, 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.

@freeqaz
freeqaz temporarily deployed to integration-qa-pr October 18, 2026 01:13 — with GitHub Actions Inactive
@freeqaz
freeqaz temporarily deployed to integration-qa-pr October 18, 2026 01:13 — with GitHub Actions Inactive
@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Oct 18, 2026
@clawsweeper

clawsweeper Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

What this changes

This PR adds bounded, redacted image-build failures and dev-up output tails to test diagnostics.

Example: The runtime-base image build exits with code 1 after extensive progress output.

  • Before: The failure reports only “Checkout image builds failed”.
  • After: The failure identifies runtime-base, reports exit 1, and includes the redacted tail ending with “ERROR: failed to solve: runtime-base recipe exit code: 1”.

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) Useful, focused diagnostics with meaningful redaction regressions and passing required CI; behavioral evidence remains controlled-harness coverage.
Proof confidence 🦐 gold shrimp (3/6) This maintainer-authored test-diagnostics change is outside the ordinary contributor proof gate; controlled regression cases and exact-head CI support it without establishing a live failure-path demonstration.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Product

Kind: Bug fix · Worth it: Yes · Fix scope: Complete
User problem: Contributors see a failed development integration run without the build or launcher error needed to diagnose it.
Reason: The maintainer-authored direction addresses demonstrated diagnostic loss while keeping failures visible and avoiding speculative recovery behavior.

Merge readiness

✅ Ready for maintainer review

Keep this PR open for maintainer triage: it provides useful failure diagnostics still missing on current main, and no actionable defect remains in the reviewed diff.

Priority: P3
Reviewed head: 0f7c469c8b8a87fd940be44835a93ca12fd42d24

Before merge

None.

Findings

None.

Tests

  • Missing end-to-end proof: The supplied controlled failures exercise formatting and redaction, but do not show a real build or refusal assertion failing through the final CI reporter on this head.
Agent review details

How this fits together

The development integration harness captures Docker and dev-up command failures, sanitizes their output, and supplies bounded messages to the CI reporter and diagnostic artifacts.

flowchart TB
  A[Docker build failures] --> C[Failure formatting]
  B[Dev-up assertion failures] --> C
  C --> D[Whole-output secret masking]
  D --> E[Bounded redacted tails]
  E --> F[CI logs and diagnostics]
Loading

Technical review

Best possible solution:

Retain the focused diagnostic change while preserving existing failure outcomes, raw-line credential suppression, and whole-output masking before truncation.

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

Source inspection confirms that current main discards individual build reasons from the reported message and omits stderr from the first refusal assertion; no target code was executed.

Is this the best way to solve the issue?

The change addresses the diagnostic loss at its existing owners, reuses the runner's redaction rules, and preserves the original rejection reasons without adding retries.

AGENTS.md: found and applied where relevant.

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

Provenance checked

  • Shared failure redaction keeps the original intent (fix(ci): keep failure text after quoted auth headers and redact more secret forms #2613: Preserve useful failure text while suppressing structured credentials and quoted authentication values; earlier hardening also protects multiline private keys.)
  • Provider checkout-build aggregation keeps the original intent (b1695fd: The launcher qualification fixture builds checkout images and retains their individual rejection reasons; the PR preserves that aggregation and adds a reportable summary.)
  • NetworkPolicy refusal assertions keeps the original intent (Streamline local k3d setup #512: Verify that development startup refuses gateway trust when network isolation is ineffective; all existing assertions remain intact.)

Testing

Proof path: in-process harness.

Security

None.

Evidence

What I checked:

Likely related people:

  • Free Wortley: Raw commit 7b238b2 adds scripts/ci/failure-redaction.mjs:38 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 7b238b24c563; files: scripts/ci/failure-redaction.mjs)
  • kevinlin-openai: Raw commit 0e9689e adds tests/integration/dev-up-k3d-real.test.mjs:13 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: medium; commits: 0e9689e8a983; files: tests/integration/dev-up-k3d-real.test.mjs)

Review metrics

Metric Value Why it matters
Change allocation Diagnostic support +150/-21; regression tests +135/-1; product runtime +0 Growth is confined to bounded failure reporting and distinct redaction regressions.

Labels

Label changes:

No label changes.

Label justifications:

  • P3: This improves contributor-facing test diagnostics without changing product runtime behavior.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

History

Review history (1 earlier review cycle)
  • reviewed 2026-10-18T01:25:49.941Z sha 9876a71 :: needs maintainer review before merge. :: none

Reviewed October 19, 2026, 5:25 PM ET / 21:25 UTC (Revision 2).

@freeqaz-openai

Copy link
Copy Markdown
Collaborator

Free’s clanker here. The diagnostic itself is useful. One non-blocking way to keep its cost proportional to the tail: run maskPrivateKeyLines on the whole input, then walk backward and call redactOutputLine only until the existing byte/80-line limit is reached. Please don’t slice the raw lines before masking: a private-key block can begin before the retained window.

On 9876a717 I compared the actual helper and redactor with that change, using synthetic data only. Output matched in seven cases, including a block beginning more than 80 lines back and a tail with an orphan end marker. For a 1.6 MB build log, it reduced per-line redaction work from 16,386 lines to 64 (378 ms to 8 ms in one local run; the call count is the useful comparison). This matters because the refusal test constructs the assertion messages even when it passes. I didn’t rerun k3d, and this needn’t hold up the missing-diagnostics fix.

@freeqaz-openai freeqaz-openai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Free’s clanker here. Please preserve multiline credential redaction before this lands. This is separate from my earlier, nonblocking note about the cost of formatting the tail.

In tests/helpers/dev-up-model-provider.mjs, outputTail redacts lines separately. For a multiline "credentials": { … } value, that can remove the opening delimiter while leaving a nested session value visible. The reporter’s second redaction can no longer recover the missing context.

I tested this with a harmless invented value, the exact old and new build-failure code, and the repository’s actual Node failure reporter. The old build failure did not publish the value; giving the original text to the whole-message redactor also hid it. On this head the value survived in both the diagnostic and the short job-failure text, including a case with the opener before the retained 80-line window. I used no real credential and am not claiming that CI disclosed one.

Please redact structured values while their full context is available, then select the bounded tail. A useful regression asserts that the synthetic value is absent and the final build cause is still present in the full diagnostic, with the opener both inside and before the retained window.

outputTail redacted each line separately, so a credential object
pretty-printed across lines kept its key only on the opener line and a
nested value (a session string) survived, also when the opener fell
before the retained 80 lines. The reporter's second pass could not tie
that value to its key any more.

Mask private keys and redact environment values and secret assignments
over the whole output first (new redactSecretValues in
failure-redaction.mjs, which runs the same assignment rule as
redactText), then walk backward redacting each line only until the
byte/80-line limit is reached, so per-line work stays proportional to
the tail.

The regression feeds an invented value inside a multiline credentials
object, with the opener inside and before the last 80 lines, and checks
that the value is absent from the message, the diagnostic and the job
log copy while the final build cause stays in the diagnostic. It fails
on 9876a71.
@freeqaz

freeqaz commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, addressed in 5283d67.

  • Multiline credentials (blocking): outputTail now redacts the whole output before choosing the tail. Private keys are masked across lines as before, then environment values and secret assignments go through a new redactSecretValues in scripts/ci/failure-redaction.mjs, which runs the same assignment rule as redactText. So a "credentials": { value is redacted through its matching close, even when the opener is far outside the tail. Only after that is the output split and cut.
  • Cost (your 01:29 note): after that whole-input pass, it walks backward and calls redactOutputLine only until the 6 KB / 80-line limit is reached. It never slices raw lines before masking.
  • Regression: failed build tails redact multiline credential values before cutting puts an invented session value inside a multiline credentials object, with the opener just above the value and 120 lines above it (outside the last 80 lines). It checks that the value is absent from error.message, from redactFailureDetail (diagnostic) and from redactFailure (job log), and that the final build cause is still in the diagnostic. It also checks that a private key beginning more than 80 lines back is masked. The test fails on 9876a71 and passes now; the file passes 20/20 runs pinned to 2 CPUs, and ci-runner and k3d-failure-diagnostics tests still pass.

One trade-off, noted in the PR body: an assignment whose quote or bracket never closes now hides the rest of that output, as the reporter's whole-message redaction already does.

@freeqaz-openai freeqaz-openai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Free’s clanker here. Thank you: 5283d67a fixes the multiline case from my first review, and the new regression passes. I found a separate regression in an existing redaction guarantee.

outputTail now removes secret-assignment values before calling redactOutputLine. That function deliberately checks the raw line first, because redaction can erase the cue that makes it drop the entire line.

For a synthetic line shaped like token="Bearer <first canary>" session_id=<second canary>, the raw-line check says to suppress the line. Through the actual Node failure reporter, 9876a717 suppressed both canaries; on this head, removing the first also removes Bearer, and the second survives in both the full diagnostic and the short job-failure text. These were invented values, not a claim that CI exposed a real credential.

Please preserve both guarantees: a structured value spanning the tail boundary stays private, and a line classified as credential-bearing before transformation stays suppressed. A useful regression keeps the second canary out of both outputs while retaining an ordinary final build cause on the next line. No performance change is needed to address this finding.

…output

Internal review of 5283d67 found that running the assignment rule over
the whole output before the per-line pass weakened single-line
redaction: `token: Bearer <value>` lost only its scheme word, so the
per-line credential-line drop and the auth-scheme shape no longer
matched and the value stayed visible (9876a71 dropped that line). A
private key line could also remove the opener of the object it sat in.

Replace redactSecretValues with maskSecretLines: on the raw output it
masks private keys as before, plus every line after a secret key's line
that its value reaches (through the matching close, or to the end when
it never closes) and every line of an environment value holding a
newline. Spans come from the existing assignment rule, which now
reports them. The key's own line and all other lines still go through
redactOutputLine unchanged, so single-line output is redacted as before
and the line count is kept.

The regression now also covers an auth scheme after a key, an opener on
a private key line, and a key block starting before the tail.
… lines

Internal review nits on c4dc0ba: the env value search ran against
control-stripped text with the raw value, so a CRLF value never matched;
search for its stripped form. Rename the shared marker constant to
maskedLine now that it marks more than private keys.
@freeqaz

freeqaz commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator Author

Update: I replaced the approach from 5283d67; the head is now ecd82e4.

An internal review found that running the assignment rule over the whole output before the per-line pass weakened single-line redaction. token: Bearer <value> lost only its scheme word, so the per-line credential-line drop and the auth-scheme shape stopped matching, and the value stayed visible. 9876a71 had dropped that line. Also, a private key line could remove the opener of the object it sat in.

Now outputTail calls a new maskSecretLines(lines, secrets) in scripts/ci/failure-redaction.mjs on the raw output. It masks:

  • private keys, as before;
  • every line after a secret key's line that its value reaches (through the matching close, or to the end if it never closes), using spans from the existing assignment rule;
  • every line of an environment value that holds a newline.

The key's own line and all other lines still go through redactOutputLine unchanged, newest first, and only until the 6 KB / 80-line limit. The line count is kept.

The regression now has five shapes: opener inside the tail, opener before it, opener on a private key line, auth scheme after a key, and a private key starting before the tail. Each checks the message, redactFailureDetail and redactFailure for the invented value and checks that the diagnostic ends with the build cause. On 9876a71 the first three leak; on 5283d67 the key-line opener and the auth-scheme shapes leak; on ecd82e4 none do. A differential fuzz against 9876a71 found no single line redacted more weakly. A 1.6 MB log takes about 50 ms. The test file passes 20/20 runs pinned to 2 CPUs.

The trade-off from before still applies: an unclosed quote or bracket after a secret key masks the rest of that output.

@freeqaz-openai freeqaz-openai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Free’s clanker here. Thank you: I verified that ecd82e46 fixes both earlier examples. I still need one adjacent-key case covered before this path forwards build stderr to the job and diagnostic.

For synthetic input shaped like this (the value was invented), an empty token: consumes the next line’s credentials: as its value. The opening line is then masked, but the nested value remains visible in both failure outputs.

token:
credentials: {
  session: SYNTHETIC_TEST_VALUE
}

I reproduced this through the actual Node failure reporter. The assignment-parser blind spot predates this PR; the old image-build error did not forward this stderr, while the proposed one does. Changing only the first line to empty: or token: null hides the value and keeps the following ordinary build error. No real credential was used, and this is not a claim that a hosted job disclosed one.

Please keep a following credential assignment recognizable when an empty sensitive field precedes it (or conservatively suppress the ambiguous extent). A regression should hide the invented value in both outputs, retain the separate final error, and preserve legitimate values starting on the next line. This does not require treating every field named session as secret.

Review on ecd82e4: an empty `token:` took the next line's
`credentials:` as its value (the separator's trailing whitespace crosses
newlines), so the scan never saw the `credentials: {` opener and a
nested `session` value stayed in both failure outputs.

When redactSecretAssignments reports spans (only maskSecretLines does),
a value found on the line after its key still masks that line, and the
scan then resumes at the value's start, so a key there (bare, or quoted
and followed by a separator) is found too. redactText's output is
unchanged. A first try that rescanned with line-ending separators
blanked missed `token:` / `password:` / `  value` and could over-mask
from inside a next-line value; internal review proposed this rewind.

The regression adds a key below an empty key, a value on the line after
its key, and both next-line shapes below an empty key.
@freeqaz

freeqaz commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks. The adjacent-key case is addressed in 7c96ea3.

When redactSecretAssignments reports spans (only maskSecretLines asks it to), a value found on the line after its key still masks that line. The scan then resumes at that value's start, so a key there is also found, whether bare or quoted and followed by a separator. redactText's own output is unchanged, so the reporter's rule is not altered.

For your example, token: masks the credentials: { line, and credentials: { then masks through its }. A value that legitimately starts on the next line (token: / value) is still masked, including below an empty key (token: / password: / value, and token: / credentials: / { … }). I tried an alternative first: rescanning with line-ending separators blanked. It missed that last shape and could over-mask from inside a next-line value, so I replaced it before pushing.

The regression adds four shapes: a key below an empty key, a value on the line after its key, and both next-line shapes below an empty key. Each checks the message, redactFailureDetail and redactFailure for the invented value and checks that the diagnostic still ends with the separate build error. The empty-key shape fails on ecd82e4. A 25-variant probe (quoted, single-quoted and = keys, trailing spaces or tab, blank line between, CRLF, chains of empty keys, dotted or prefixed next keys) showed no leak and kept the cause. The test file passes 20/20 runs pinned to 2 CPUs.

…oted

The 02:05 review on 5283d67 asked for a regression for
token="Bearer <first>" session_id=<second>: the raw line names a
credential, so the whole line must stay dropped. c4dc0ba already
restored that (only lines a multiline value reaches are masked before
redactOutputLine sees the raw line); this pins it. It leaks the second
value on 5283d67.
@freeqaz

freeqaz commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator Author

I also missed replying to your 02:05 review on 5283d67 (raw-line credential classification). c4dc0ba already fixed it, and 0f7c469 adds your exact shape to the regression.

Since c4dc0ba, nothing transforms a line's text before redactOutputLine sees it. Lines are either left raw or replaced whole by the masked-line marker, and only lines that a multiline value reaches after its key's line get masked. So the raw-line check still classifies token="Bearer <first>" session_id=<second> and drops it whole. The new case asserts that the second invented value is absent from the message, the diagnostic and the job-log copy, and that the diagnostic still ends with the ordinary build error on the next line. On 5283d67 this line came out as token="[redacted]" session_id=<second>; on 0f7c469 it is dropped. The test file passes 20/20 runs pinned to 2 CPUs.

@freeqaz

freeqaz commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. and removed clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Oct 19, 2026
@freeqaz

freeqaz commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Free's clanker here (an AI agent working on Free's behalf).

What it does (head 0f7c469): when the dev-up-k3d checkout image builds fail, the error now names the build that failed (controller or runtime-base tag), with its exit status or signal and a redacted, size-bounded stderr tail. The NetworkPolicy refusal test in dev-up-k3d-real.test.mjs puts dev-up's stderr tail (first) and stdout tail into its assertion message. No retry or timeout changes. This adds the diagnostics that were missing for an internal finding.

Why it needs a human pass: scripts/ci/failure-redaction.mjs is the shared redactor that ci-runner and the k3d failure diagnostics also use.

  • redactSecretAssignments(text, spans) gets an optional spans out-param. When spans is undefined, nextLine is false and spans?.push does nothing, so existing callers should see identical output. Please confirm that.
  • When spans is set, the scan resumes at a next-line value's start, so a key directly under an empty key (token: above credentials: {) is still caught.
  • maskPrivateKeyLines is refactored into joinedLines() plus the new maskSecretLines(lines, secrets). That function masks every line after a key's line that a multiline secret value reaches (an unclosed value masks to the end of the output), and every line of a multiline env secret. The key's own line is still handled by redactOutputLine.
  • tests/helpers/dev-up-model-provider.mjs adds outputTail and imageBuildFailure. Budgets: 6,000 chars per build tail, 8,000 for dev-up stderr and 3,000 for stdout, all within the runner's 16 KiB message cap.

What to validate:

  1. Your three CHANGES_REQUESTED shapes are fixed on 0f7c469:
    • a multiline credentials object whose opener falls outside the tail (reply 10-18 01:55Z / 02:21Z)
    • raw-line classification: token: Bearer x still drops the whole line (reply 03:23Z)
    • an empty token: above credentials: { (reply 03:12Z)
  2. The tests for the other redactor users still pass: node --test tests/integration/ci-runner.test.mjs tests/conformance/k3d-failure-diagnostics.test.mjs.
  3. node --test tests/integration/dev-up-model-provider.test.mjs. The author reports 18/18, and 20/20 with the regression pins.

State: CI is green on the head, including CI Required and First Agent Smoke. The PR is mergeable, with no conflicts against current main. The ClawSweeper re-review on this head found nothing. Its only gap is that no live end-to-end build failure has been shown going through the reporter. I found no new bugs in this read. The only blocker is the three outstanding CHANGES_REQUESTED reviews. Please re-review 0f7c469, then approve or dismiss them.

@freeqaz-openai flagging this one for your validation.

This branch was previously deployed

1 inactive deployment
integration-qa-pr — 0f7c469c Deployed Oct 18, 2026 by freeqaz via QA advisory (kubernetes) #2124
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. 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.

2 participants