Skip to content

fix(pi): stop an unproductive nudge loop, and name every agent abort - #1273

Draft
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-self-driving/fixpi-stop-an-unproductive-nudge-loop-c79bd2
Draft

posthog[bot] wants to merge 1 commit into
mainfrom
posthog-self-driving/fixpi-stop-an-unproductive-nudge-loop-c79bd2

Conversation

@posthog

@posthog posthog Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Problem

  • A run can die on the very first thing a new customer does with PostHog, and the retries walk them into the account run limit.
  • The pi completion guard re-prompts while tasks stay open, but never checks that a nudge produced anything. A turn that returns with no tool call and no text comes back in milliseconds, so the guard spends its whole cap of 20 in seconds and the run aborts as PHW_AGENT_INCOMPLETE_TASKS with no work done.
  • agent aborted cannot tell us how large that slice is: over the last 14 days, 296 of 303 events carried no reason at all.
  • Worse, the event is not even measuring failures. On the anthropic path it fired whenever the SDK sent no success result — 173 of 254 unlabelled events belong to runs that also reported agent completed.
  • A gateway mint refusal reaches the handoff screen with its message dropped, so a user whose account is out of runs reads only "The Wizard's a little busy" and retries into the same wall.

Changes

Mechanism Before After
pi completion guard nudge until tasks close or the cap runs out stop on the first nudge that adds no tool call and no assistant text; wait NUDGE_BACKOFF_MS between live nudges
agent aborted (anthropic) fired on any run without an SDK success result, unlabelled fires only for a run that failed, always with failure_mode
agent aborted (linear) unlabelled carries failure_mode
agent incomplete tasks open_tasks only adds nudges and dead_nudge, so the dead-nudge slice is separable
Mint handoff screen generic "busy" copy also shows the gateway refusal message
  • The guard rule moves into nudgeWhileUnfinished in completion.ts, next to completionFailure, so the loop's caller keeps only its counters.
  • runErrorType moves to signals.ts, beside AgentErrorType, because the anthropic path now classifies its rethrow on the same rule as pi.

Note

Abort counts will drop on merge, and most of the drop is the false positives leaving, not runs getting better. Read the new failure_mode breakdown, not the raw count, across the change.

Test plan

  • src/lib/agent/runner/harness/pi/__tests__/completion.test.ts: a dead nudge stops after one send, live nudges continue, the cap holds, a finished run sends nothing, and live nudges wait.
  • src/ui/__tests__/mint-failure.test.ts: the refusal reason appears for both gateway codes and for nothing else.
  • Full suite: 178 files, 2846 tests, all passing. Lint clean on the changed files.

LLM context

Written by PostHog Desktop from the linked inbox report. Considered and rejected: lowering MAX_CONTINUE_NUDGES, which caps the damage of a dead loop but still burns the cap and still aborts a run that only needed one more live nudge. The same guard in task.ts is capped at 3 and was left alone.


Created with PostHog Desktop from this inbox report.

The pi completion guard re-prompted while tasks stayed open without
checking that a nudge produced anything. A model that returns with no
tool call and no text drains all 20 nudges in seconds, so the run aborts
with INCOMPLETE_TASKS and no work done. Stop on the first dead nudge and
space live ones out.

The `agent aborted` event also fired for runs that completed without an
SDK success result, and named no failure mode on either the anthropic or
the linear path. Emit it only for a run that failed, and always with the
mode.

A gateway mint refusal reached the handoff screen with its message
dropped, so a user out of runs read only that the wizard was busy. Show
the refusal reason there.

Generated-By: PostHog Desktop
Task-Id: 437bbb5a-d0c5-4b72-aaa9-9eff3b08e32c
@github-actions

Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci ai-observability
  • /wizard-ci basic-integration
  • /wizard-ci mcp-analytics
  • /wizard-ci replay-vision
  • /wizard-ci revenue
  • /wizard-ci self-driving
  • /wizard-ci warehouse
  • /wizard-ci warehouse-seeded

Test an individual app:

  • /wizard-ci ai-observability/anthropic
  • /wizard-ci ai-observability/google-adk
  • /wizard-ci ai-observability/groq
Show more apps
  • /wizard-ci ai-observability/manual-capture
  • /wizard-ci ai-observability/openai
  • /wizard-ci ai-observability/openai-agents
  • /wizard-ci ai-observability/opentelemetry
  • /wizard-ci ai-observability/vercel-ai
  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/flutter
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci mcp-analytics/custom-dispatcher
  • /wizard-ci mcp-analytics/typescript-sdk
  • /wizard-ci replay-vision/javascript-node
  • /wizard-ci replay-vision/next-js
  • /wizard-ci replay-vision/react-vite
  • /wizard-ci revenue/stripe
  • /wizard-ci self-driving/astro
  • /wizard-ci self-driving/fastapi
  • /wizard-ci self-driving/nuxt
  • /wizard-ci self-driving/react-router
  • /wizard-ci self-driving/sveltekit
  • /wizard-ci warehouse/monorepo-env
  • /wizard-ci warehouse/multi-source-next
  • /wizard-ci warehouse/stripe-node
  • /wizard-ci warehouse/zero-source
  • /wizard-ci warehouse-seeded/next-stripe
  • /wizard-ci warehouse-seeded/next-stripe-declined

Test against a Context Mill branch:

  • /wizard-ci all context-mill:my-branch

Add context-mill:<branch> to any command above to pin the Context Mill branch. It defaults to main.

Results will be posted here when complete.

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