Skip to content

fix(flare-output): don't fail RealLlm on a benign stdin broken-pipe - #234

Merged
getappz merged 1 commit into
masterfrom
fix/caveman-cli-broken-pipe-flake
Jul 18, 2026
Merged

fix(flare-output): don't fail RealLlm on a benign stdin broken-pipe#234
getappz merged 1 commit into
masterfrom
fix/caveman-cli-broken-pipe-flake

Conversation

@getappz

@getappz getappz commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

Root-causes the caveman_cli.rs flake that's been showing up as broken pipe failures under CI's compile-load scheduling jitter (most recently cascading into a fail-fast cancellation of #230's Windows build).

call_via_cli() writes the prompt to the child's stdin on a separate thread (correctly, to avoid the classic write/read deadlock), but then treated any error from that write — including a broken pipe from a child that already exited successfully — as fatal, checked before the child's own exit status.

A child closing its stdin read end early after a clean exit (a canned response, a CLI that doesn't need to drain the full prompt) is the ordinary short-pipe case, not a real failure. The test's stubbed claude script echos a canned response and exits immediately without touching stdin at all, so the write's completion races the child's exit — under compile-load scheduling pressure, the child sometimes wins the race.

Fix

Reorder the checks: the child's actual exit status decides success/failure. A stdin-write error only surfaces (as a non-fatal warning) once we already know the child succeeded — it no longer masks the real failure/success signal.

Test plan

  • cargo test --workspace (567 passed)
  • cargo fmt --check
  • cargo clippy -p agentflare-flare-output --all-targets --all-features -- -D warnings -A unsafe_code -A clippy::pedantic
  • Stress-tested the previously-flaky test: 22 consecutive passes across 3 runs, including one with a concurrent full-workspace rebuild running for real CPU contention (the actual trigger condition)

Summary by CodeRabbit

  • Bug Fixes
    • Improved command-line processing reliability by preserving successful output when a non-critical input write warning occurs.
    • Continued to report meaningful errors when the underlying command fails.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@getappz, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 11 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 62dcafc7-6a2f-48d9-9318-ab63fd88b203

📥 Commits

Reviewing files that changed from the base of the PR and between 2666b5b and ca3ce35.

📒 Files selected for processing (1)
  • crates/flare-output/src/llm.rs
📝 Walkthrough

Walkthrough

call_via_cli now evaluates the claude --print process status before handling stdin writer results. Process failures remain fatal with captured stderr, while stdin write failures after successful execution produce a warning and return trimmed stdout.

Changes

CLI error handling

Layer / File(s) Summary
Prioritize process status
crates/flare-output/src/llm.rs
call_via_cli preserves child-process errors and downgrades stdin write failures to warnings when the process succeeds.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: ignoring benign stdin broken-pipe failures after a successful child exit.
Description check ✅ Passed The description covers the summary, fix, and test plan, with only the reviewer-notes section left unfilled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/caveman-cli-broken-pipe-flake

Comment @coderabbitai help to get the list of available commands.

@getappz
getappz enabled auto-merge (squash) July 18, 2026 04:50
@getappz
getappz force-pushed the fix/caveman-cli-broken-pipe-flake branch from 2666b5b to d4db010 Compare July 18, 2026 04:56
call_via_cli() writes the prompt to the child's stdin on a separate
thread (to avoid the write/read deadlock) but then treated ANY error
from that write -- including a broken pipe from a child that already
exited successfully -- as fatal, even before checking whether the
child itself succeeded.

A child closing its stdin read end early after a clean exit (a canned
response, or any CLI that doesn't need to drain the full prompt) is
the ordinary short-pipe case, not a real failure. This is exactly what
made tests/caveman_cli.rs's stubbed-claude test flaky under CI's
compile-load scheduling jitter: the stub echoes a canned response and
exits immediately without reading stdin, so the write's completion
raced the child's exit.

Reorders the checks so the child's actual exit status is what decides
success/failure; a stdin-write error only surfaces as a warning once
we already know the child succeeded. Stress-tested 22 consecutive runs
of the previously-flaky test, including under concurrent compile load.
@getappz
getappz force-pushed the fix/caveman-cli-broken-pipe-flake branch from d4db010 to ca3ce35 Compare July 18, 2026 05:09
@getappz
getappz merged commit f92c38f into master Jul 18, 2026
16 checks passed
@getappz
getappz deleted the fix/caveman-cli-broken-pipe-flake branch July 18, 2026 05:14
getappz added a commit that referenced this pull request Jul 29, 2026
…icate-item reuse, assignee freeze (#365)

* feat(handoff): verified continuation commit, structured payload, duplicate-item reuse, assignee freeze

Item #236 (handoff hardening, QuorumGit adoption):

- HandoffRequest gains last_commit: verified via `git cat-file -e` and,
  when the item's task/<seq> branch already exists, `git merge-base
  --is-ancestor` -- a fabricated or unreachable OID is rejected, not
  trusted.
- HandoffRequest gains required completed/remaining and optional
  blockers fields, stored in the asset's metadata.
- A handoff without item_id now reuses an existing open item assigned
  to the recipient (matched by name or thread_id) instead of blindly
  creating a duplicate; genuinely new work still auto-creates.
- agentflare_backend::item::claim gains a BlockedByAssignee outcome:
  a freshly handed-off, never-claimed item can only be claimed by its
  assignee until accepted, excluding completed/cancelled items.

Deferred: shim-side branch-push denial for open handoffs (spec's own
review already downgraded this to defense-in-depth, covered in large
part by #234's scope enforcement and the opencode branch-guard plugin).

* fix(handoff): fmt, canonicalize claim owner, validate commit oid/payload off the DB lock (#366)

- cargo fmt (item.rs claim closure, types.rs schemars doc attr)
- item::claim: canonicalize both sides of the assignee/owner comparison so
  an alias owner (claude:1) isn't wrongly BlockedByAssignee against its own
  canonical handoff assignee (claude-code)
- verify_continuation_commit: resolve the target branch under the backend
  DB lock, then run all git subprocess checks after releasing it, matching
  the existing item_claim split; validate oid is a plain hex id and force
  commit-type resolution (oid^{commit}) so a non-hex/flag-like value or a
  blob/tree/tag can't pass as a continuation commit
- handoff_impl: reject empty completed/remaining instead of silently
  accepting an empty structured payload
- mcp_prompts: document completed/remaining as required handoff fields
  so generated requests don't fail deserialization

* fix(test): stop with_temp_home_clears_the_override_env_var_after_returning from racing concurrent with_temp_home callers

The assertion read AGENTFLARE_HOME_OVERRIDE outside GLOBAL_STATE_LOCK,
the lock that guards every set/remove of it. A concurrent thread's own
correctly-scoped with_temp_home call could be transiently holding the
var set at the exact moment this test read it, flaking the check on a
var this test was never entitled to observe in the first place. Fix:
acquire the same lock before reading.
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.

1 participant