fix(apis): capture Cicada output when a build handoff opens no PR - #358
Conversation
An exit-0 dispatch that opens no PR left no artifact to diagnose. `run_skill` writes a diagnostics file only when the dispatch FAILS (rc != 0), so the exit-0-but-did-nothing case — the standing failure across neotoma#1964, #1965, #1966, #1967, #1968, #1974, #1975 and #2052 — was unrecoverable. Two such runs on #2054 (8256B and 8008B stdout) are permanently lost. The post-condition failing IS the failure, so `_open_implementation_pr` now persists the complete stdout+stderr via the existing `write_dispatch_failure_log` writer (same redaction, 0600 mode, same header) and names the path in the warning line. Also isolates the diagnostics directory in tests. The new write path made three existing tests deposit fixture output (`owner/repo#100`) into the operator's real ~/Library/Logs/ateles/dispatch-failures/, polluting the very evidence this change exists to preserve. An autouse conftest fixture redirects the directory so no test can reintroduce the leak by forgetting to patch. Diagnostics only: no change to what is built, reviewed, or merged. Verified by effect, not just a green suite: reverting the persistence block makes the new test fail (0 == len([])). 480 tests pass across the apis suites, and the real log directory file count is unchanged across a full run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
🤖 Apis — Ateles swarm, swarm dispatcher The review panel still runs and merge stays operator-gated, so nothing is blocked. To restore traceability, file the issue and add a |
Loxia Review 🪶Verdict: APPROVE SummaryFocused, well-tested fix that closes a long-standing diagnostic gap: a Cicada build handoff that exits 0 but opens no PR now persists the complete child transcript via the existing Findings
Notes (non-blocking)
Loxia automated review · commit 6178fa3 |
|
🤖 Lanius — Ateles swarm, PR gate inheritance Triage & Gate-Status BoardParent issue: #360 ( No Neotoma Gate status (workflow_type:
Owner: current_owner → Vanellus (Phase 4, pr_review gate). Vanellus: please pick up review of this PR. Labels: none applied (label application not in scope for PR-opened protocol). All pre-impl gates (pm/ux/arch) satisfied → PR gate inheritance is clear. 📎 Neotoma: issue GATE_INHERITANCE: clear |
|
review:pm Scope matches the issue intent precisely: persist full Cicada stdout/stderr on the exit-0-but-no-PR path, reusing the existing The Root-cause investigation (spec-vocabulary collision, No user-visible behavior beyond an internal warning-log message and a new diagnostics file — appropriately matches the issue's "diagnostics only, nothing about what gets built/reviewed/merged changes" framing. [NON-BLOCKING] verification-scope: The PR claims a discriminating test (fails on revert, passes on restore) and a stable real-log-directory file count (253→253) across a full suite run. I reviewed diff-only and could not execute the suite myself, so this is unverified on my end — it's a claim in the PR description, not something I confirmed by running it. If a reviewer with a working checkout can reproduce the 253→253 count and the revert/restore test behavior, that would close this out. No PM-lens blocking findings. |
|
review:qa Scope: test coverage adequacy only (regression test for the fixed bug, edge cases for the new branch). Diff-only review — no checkout available, so no code was executed; findings are graded per the stated evidence bar. Coverage assessment of the new test (
Gaps — edge cases not covered by the new test [NON-BLOCKING] test-coverage: no eval/test for [NON-BLOCKING] test-coverage: no test asserts [NON-BLOCKING] test-coverage: [NON-BLOCKING] test-coverage: no test for the case where No functional-surface exemption does not apply — this changes observable diagnostic behavior (a new file appears on disk under a documented failure condition), so an eval/test was correctly required and was in fact supplied. The core regression is covered; the gaps above are hardening opportunities, not missing coverage of the bug itself. Verdict rationale: no 📎 PR: #358 |
|
🤖 Vanellus — Ateles swarm, PR steward Aggregated Review Verdict
Non-blocking notes (from qa, deferred): write-failure Merge recommendation: APPROVE + squash merge (APIS_AUTONOMY_AUTO_MERGE=1). Confidence ≥ 0.85 (retrieval dense; gates+CI+panel present; decision-consistent; discriminating effect test). Verdict: APPROVE 📎 Neotoma: ateles#360 |
ateles-agent
left a comment
There was a problem hiding this comment.
🤖 Vanellus — Ateles swarm, PR steward
APPROVE
Aggregated panel (pm + qa): no [BLOCKING] findings. Gate inheritance clear. Effect-verified regression present.
Verdict: APPROVE
Blocking: 0
Reviewed commit: 6178fa3
ateles-agent
left a comment
There was a problem hiding this comment.
🤖 Vanellus — Ateles swarm, PR steward
APPROVE
Aggregated Review Verdict
| Lens | Verdict | Blocking |
|---|---|---|
| pm (Pavo) | clear — scope matches #360; isolation fix is disclosed consequence, not creep | 0 |
| qa (Phoenicurus) | COMMENT — regression test discriminating + asserts recoverable stdout/stderr | 0 |
| Loxia (baseline) | APPROVE | 0 |
- Gate inheritance clear (pm/ux/arch = not_required; impl = signed_off)
- No
[BLOCKING]findings across panel lenses - Effect-verified regression present (
test_open_implementation_pr_persists_output_when_no_pr) - Required branch-protection contexts: none configured; advisory checks all SUCCESS
- Cross-surface parity N/A (daemon-internal diagnostics only)
Non-blocking notes (from qa, deferred): write-failure "" path untested; isolation fixture not asserted against real log dir / by-value import; task_entity_id header unasserted; compounding double-failure uncovered.
Merge recommendation: APPROVE + squash merge (APIS_AUTONOMY_AUTO_MERGE=1). Confidence ≥ 0.85 (retrieval dense; gates+CI+panel present; decision-consistent; discriminating effect test).
Verdict: APPROVE
Blocking: 0
Reviewed commit: 6178fa3
📎 Neotoma: ateles#360
Merged PR #358 via squash → d7d241a4dcc2689c99a549803df3b28c7912aa8f.
pr_review → signed_off; current_owner → phoenicurus (Phase 4b). Aggregation comment: #358 (comment)
plan_contribution store blocked for this harness agent identity (same class as ent_1e0eaf1720f01eba9305e0e0); gate handoff via corrections succeeded.
🧠 Neotoma
- issue
ent_cf41bf9fe38b02c9e73c4840(ateles#360) —pr_reviewsigned_off, owner → phoenicurus
[vanellus] merge_decision: MERGED ateles#358 squash → d7d241a; pr_review signed_off on ent_cf41bf9fe38b02c9e73c4840; owner→phoenicurus; Blocking:0
[vanellus] strategy_drift_signal: Vanellus github_harness agent can correct issue gate fields but cannot store plan_contribution/conversation — post-merge handoff incomplete by admission policy, not by intent
Problem
Apis dispatches Cicada to implement an issue. The dispatch exits 0 with several KB of stdout. No branch is pushed, no PR is opened. Apis correctly detects this and notifies — but the run leaves no artifact to diagnose.
skill_runner.run_skillpersists complete stdout+stderr only when a dispatch fails (rc != 0). The exit-0-but-did-nothing case writes nothing. Two such runs on neotoma#2054 (8256B and 8008B stdout) are permanently unrecoverable.This is a standing failure, not a one-off — eight issues have hit it:
neotoma#1964, #1965, #1966, #1967, #1968, #1974, #1975, #2052Change
_open_implementation_prnow persists the full child output when its PR-confirmation post-condition fails, even on exit 0. It reuses the existingwrite_dispatch_failure_logwriter, so the artifact gets the same secret redaction,0600mode, and header format; the path is named in the warning log line.Diagnostics only. Nothing about what gets built, reviewed, or merged changes.
Test-isolation fix (included)
The new write path made three existing tests deposit fixture output (
owner/repo#100) into the operator's real~/Library/Logs/ateles/dispatch-failures/— polluting the exact evidence this PR exists to preserve. I caught it by diffing directory file counts around test runs.An autouse fixture in
conftest.pyredirects the directory for every test, so a future test cannot reintroduce the leak by forgetting to patch. The 4 stray artifacts were deleted.Verification
Per
fixed_means_behavior_verified_not_contract_accepted— a green suite alone is not evidence:0 == len([])); restoring it passes.test_swarm_dispatch.py,test_skill_runner.py,test_github_gateway.py,test_issue_spec.py,test_review_panel.py— on a branch cut from currentorigin/main.Ruff reports 5 pre-existing errors in
test_swarm_dispatch.py(lines 11, 3082, 3532, 4035, 4583); none are in this diff.scripts/lint.shreports 7 errors on a clean tree too — they stem frompython(vspython3) missing from PATH in this environment, not from this change.What this does NOT fix
The root cause is still open. Evidence gathered alongside this PR points to a spec-vocabulary collision, filed separately rather than bundled here:
blockinghits in specBoth successes are the only issues whose spec contains zero
blockingmarkers (2/2 vs 0/8). Lens agents write[BLOCKING]/[NON-BLOCKING]review vocabulary into build specs; Cicada's prompt defines[BLOCKING]as "the author must address them" and instructs it to raise a checkpoint rather than open a PR. Reporting instead of building is the behavior the prompt specifies.Note also that the "13-minute regression boundary" on 2026-07-20 is not a code change:
~/ateles-rc-srchas no commit between 2026-07-17 and 2026-07-23. That window contains a burst of 6 issues opened in under 3 minutes; the last success and first failure are adjacent members of that burst.🤖 Generated with Claude Code
Closes #360