Skip to content

fix(harness): extend startup retry to push events; add Turns=0 driver-handoff diagnostics#47384

Merged
pelikhan merged 4 commits into
mainfrom
copilot/deep-report-isolate-tidy-failure
Jul 22, 2026
Merged

fix(harness): extend startup retry to push events; add Turns=0 driver-handoff diagnostics#47384
pelikhan merged 4 commits into
mainfrom
copilot/deep-report-isolate-tidy-failure

Conversation

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

The exit-code-2 startup retry (MAX_SCHEDULED_EXIT2_RETRIES=1) only fired for schedule-triggered runs. Push-triggered agentic workflows (e.g. Tidy on push/main) that hit a transient Copilot API startup failure exited immediately with Turns=0 — no retry, deterministic red.

Changes

copilot_harness.cjs — extend retry eligibility to push

  • Introduces isStartupRetryEligible = isScheduledRun || GITHUB_EVENT_NAME === "push", replacing the isScheduledRun guard on the exit-code-2 retry block and on emitInfrastructureIncomplete
  • Retry log message now includes the trigger label (scheduled vs push) and the driver-handoff Turns=0 marker for traceability
// Before
if (isScheduledRun && result.exitCode === 2 && !result.hasOutput && ...)

// After
const isStartupRetryEligible = isScheduledRun || process.env.GITHUB_EVENT_NAME === "push";
if (isStartupRetryEligible && result.exitCode === 2 && !result.hasOutput && ...)
  • Adds a driver-handoff-turns0 diagnostic log whenever any attempt produces no output, making the signature scannable across all event types

copilot_sdk_session.cjs — surface turn count at session end

  • Adds assistantTurnCount counter incremented on each assistant.message event
  • Appends assistantTurns=N to all session completed: log lines; Turns=0 sessions are now directly visible without cross-referencing JSONL
session completed: hasOutput=false assistantTurns=0 durationMs=1234

copilot_harness.test.cjs — update and extend retry tests

  • Renames the isScheduledRun test parameter to isStartupRetryEligible
  • Adds tests: push-event startup retry fires once; non-eligible triggers (e.g. pull_request) do not retry

Generated by 👨‍🍳 PR Sous Chef · gpt54 6.54 AIC · ⌖ 7.98 AIC · ⊞ 7K ·
Comment /souschef to run again

…gnostics

- Extend exit-code-2 startup retry (MAX_SCHEDULED_EXIT2_RETRIES=1) to push-triggered
  runs via new isStartupRetryEligible flag (schedule OR push)
- Add driver-handoff-turns0 diagnostic log when no output is produced after an attempt,
  making the Turns=0 signature visible in run logs for cross-engine triage
- Update emitInfrastructureIncomplete to fire for push runs (not just schedule), with
  an explicit reference to the Turns=0 driver-handoff failure signature
- Add assistantTurnCount counter in copilot_sdk_session.cjs, incremented on each
  assistant.message event, logged at session completion (hasOutput + assistantTurns + durationMs)
- Update tests: rename isScheduledRun param to isStartupRetryEligible, add push retry
  test and non-eligible-trigger test

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Isolate the deterministic Tidy x2 failure on push/main fix(harness): extend startup retry to push events; add Turns=0 driver-handoff diagnostics Jul 22, 2026
Copilot AI requested a review from pelikhan July 22, 2026 19:37
@pelikhan
pelikhan marked this pull request as ready for review July 22, 2026 20:28
Copilot AI review requested due to automatic review settings July 22, 2026 20:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Extends transient Copilot startup recovery to push-triggered workflows and adds Turns=0 diagnostics.

Changes:

  • Enables one fresh retry for push startup failures.
  • Logs driver-handoff and SDK assistant-turn diagnostics.
  • Adds retry-policy tests.
Show a summary per file
File Description
actions/setup/js/copilot_harness.cjs Extends retry eligibility and diagnostics.
actions/setup/js/copilot_harness.test.cjs Adds push and non-eligible retry tests.
actions/setup/js/copilot_sdk_session.cjs Logs assistant turn counts.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Medium


const durationMs = Date.now() - startTime;
log(`session completed: hasOutput=${hasOutput} durationMs=${durationMs}`);
log(`session completed: hasOutput=${hasOutput} assistantTurns=${assistantTurnCount} durationMs=${durationMs}`);
Comment on lines +421 to +425
it("retries once for push startup interruption with exit code 2 and no output", () => {
const result = { exitCode: 2, hasOutput: false };
// push events are startup-retry-eligible; first attempt retries, second does not
expect(shouldRetry(result, 0, true, 0)).toBe(true);
expect(shouldRetry(result, 1, true, 1)).toBe(false);
Comment thread actions/setup/js/copilot_harness.cjs Outdated
Comment on lines +1406 to +1409
if (isStartupRetryEligible && lastExitCode === 2 && scheduledExit2RetryAttempted) {
const triggerLabel = isScheduledRun ? "scheduled" : "push";
emitInfrastructureIncomplete(
`Copilot API interruption (exit code 2) persisted after automatic retry in ${triggerLabel} workflow run. ` + "This is the Turns=0 driver-handoff failure signature. Check the agent-stdio.log for startup diagnostics."
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #47384 does not have the 'implementation' label and has 0 new lines of code in business logic directories (threshold: 100).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review

The fix is directionally correct — extending startup retry eligibility to push events addresses the Turns=0 deterministic failure on push-triggered workflows. Three blocking issues identified by prior inline comments remain unaddressed:

  1. assistantTurnCount not incremented in sendAndWait fallback (copilot_sdk_session.cjs) — sessions completing via that branch log assistantTurns=0 despite having output, making the diagnostic actively misleading.

  2. Push-event retry test doesn't exercise GITHUB_EVENT_NAME (copilot_harness.test.cjs) — the test passes a precomputed true flag; the production guard process.env.GITHUB_EVENT_NAME === "push" is never covered.

  3. emitInfrastructureIncomplete can fire on successful retry (copilot_harness.cjs) — the guard fires whenever lastExitCode === 2 && scheduledExit2RetryAttempted, even if the retry succeeded and a later attempt failed for an unrelated reason.

Please address the three issues flagged in the inline comments before merging.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 18.6 AIC · ⌖ 7.35 AIC · ⊞ 5K

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review

Applied /diagnosing-bugs and /tdd — requesting changes on issues already flagged inline by Copilot.

Key issues:

  1. assistantTurnCount not incremented in fallback paths (sendAndWait, idle-timeout, post-completion watchdog) — sessions that produced output will log Turns=0, defeating the diagnostic.
  2. Push-event test passes a precomputed bool, so it never exercises the GITHUB_EVENT_NAME === 'push' expression itself.
  3. emitInfrastructureIncomplete fires based on final exit code + retry flag alone — a retry that succeeds then later errors still triggers the false-positive message.

Positive highlights:

  • Clean, minimal diff scoped to the retry block
  • driver-handoff-turns0 diagnostic log is a useful traceability addition
  • Non-eligible trigger guard test is solid

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 19.4 AIC · ⌖ 4.51 AIC · ⊞ 6.7K
Comment /matt to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: non-blocking observations

The core change is correct: extending isStartupRetryEligible to push events is sound, and the driver-handoff Turns=0 diagnostics are a clear improvement. Two medium issues worth fixing before this pattern is reused:

Issues found

1. assistantTurnCount counts streaming chunks, not turns (copilot_sdk_session.cjs:148) — the counter increments on every if (content) fragment, not once per assistant.message event. This inflates assistantTurns=N and makes Turns=0 triage ambiguous. Move the increment outside the content guard.

2. triggerLabel is duplicated and will silently lie for future triggers (copilot_harness.cjs:~1374, ~1406) — isScheduledRun ? "scheduled" : "push" appears twice and produces "push" for any non-scheduled eligible trigger that might be added later. Compute it once as process.env.GITHUB_EVENT_NAME ?? "unknown" at the top of main().

Existing review comments (by @Copilot) on sendAndWait fallback missing turn-count and the emitInfrastructureIncomplete false-positive are also valid and worth addressing.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 32.3 AIC · ⌖ 4.81 AIC · ⊞ 5.7K
Comment /review to run again

Comments that could not be inline-anchored

actions/setup/js/copilot_sdk_session.cjs:148

assistantTurnCount over-counts on multi-chunk turns: incrementing inside the if (content) block counts streaming fragments, not assistant turns, inflating assistantTurns=N in the log.

<details>
<summary>💡 Suggested fix</summary>

Move the increment outside the content guard so it fires once per assistant.message event:

case &quot;assistant.message&quot;: {
  const content = event.message?.content?.text ?? &quot;&quot;;
  if (content) {
    hasOutput = true;
    output += content;
  }
  assista…

</details>

<details><summary>actions/setup/js/copilot_harness.cjs:1374</summary>

**`triggerLabel` will silently say &quot;push&quot; for any future non-push eligible trigger**: the expression `isScheduledRun ? &quot;scheduled&quot; : &quot;push&quot;` is also duplicated at ~line 1406, so both sites must be updated if `isStartupRetryEligible` ever covers a third event type.

&lt;details&gt;
&lt;summary&gt;💡 Suggested fix&lt;/summary&gt;

Compute it once from the actual event name rather than deriving from `isScheduledRun`:

```js
const triggerLabel = process.env.GITHUB_EVENT_NAME ?? &quot;unknown&quot;;

Or, if a display-frien…

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 82/100 — Excellent

Analyzed 2 test(s): 2 design, 0 implementation, 0 violation(s).

📊 Metrics (2 tests)
Metric Value
Analyzed 2 (Go: 0, JS: 2)
✅ Design 2 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 2 (100%)
Duplicate clusters 0
Inflation No (test:prod ≈ 0.5:1)
🚨 Violations 0
Test File Classification Issues
retries once for push startup interruption with exit code 2 and no output copilot_harness.test.cjs design_test ⚠️ Uses local shouldRetry helper (re-implements logic) rather than calling shouldRetryFailedExecution directly
does not retry on exit code 2 with no output for non-eligible triggers copilot_harness.test.cjs design_test None
⚠️ Flagged Tests (1)

retries once for push startup interruption with exit code 2 and no output (copilot_harness.test.cjs) — design_test, high_value. The test uses an inline shouldRetry helper that re-implements retry-eligibility logic locally rather than calling the production shouldRetryFailedExecution. This means the test verifies the local model, not the actual production function. Consider adding a companion test that calls shouldRetryFailedExecution directly with push-event inputs.

Verdict

Passed. 0% implementation tests (threshold: 30%). New push-event startup retry tests are behavioral and well-targeted. No violations found.

References:

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 48.4 AIC · ⌖ 6.74 AIC · ⊞ 7K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 82/100. 0% implementation tests (threshold: 30%). New push-event startup retry tests are behavioral and well-targeted. No violations found.

@github-actions github-actions Bot mentioned this pull request Jul 22, 2026
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

…ng tests, terminal hasOutput guard

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

All three review comments addressed in commit fix: address review comments — turn counter fallback, push-event wiring tests, terminal hasOutput guard:

  1. sendAndWait fallback turn counter (copilot_sdk_session.cjs:440): Added assistantTurnCount++ when the fallback captures content, so sessions that reach the fallback no longer log hasOutput=true assistantTurns=0.

  2. Event-to-policy wiring tests (copilot_harness.test.cjs): Extracted computeStartupRetryEligible(eventName) as an exported function from the harness and replaced the precomputed-flag push test with one that calls computeStartupRetryEligible("push") directly. Added a full computeStartupRetryEligible wiring describe block covering schedule, push, pull_request, workflow_dispatch, and undefined — removing the production eligibility clause would now break these tests.

  3. Terminal hasOutput guard (copilot_harness.cjs:1419): Added lastHasOutput tracking (updated alongside lastExitCode on each attempt). The emitInfrastructureIncomplete condition now requires !lastHasOutput, so a retry that recovers and produces output but later exits code 2 no longer emits the false Turns=0 verdict. New tests cover all four cases (terminal no-output emits, terminal has-output suppresses, no retry suppresses, non-eligible event suppresses).

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, review the latest open feedback and comments on this PR, address any unresolved items, and rerun checks once the branch is up to date.

Generated by 👨‍🍳 PR Sous Chef · gpt54 6.54 AIC · ⌖ 7.98 AIC · ⊞ 7K ·
Comment /souschef to run again

Copilot AI requested a review from gh-aw-bot July 22, 2026 21:51
@pelikhan
pelikhan merged commit 7a50782 into main Jul 22, 2026
9 checks passed
@pelikhan
pelikhan deleted the copilot/deep-report-isolate-tidy-failure branch July 22, 2026 22:03
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.

[deep-report] Isolate the deterministic Tidy x2 failure on push/main (Turns=0 driver exit)

4 participants