Skip to content

fix(build): turbo runner now actually reports per-task accounting (#830)#833

Merged
horza-bora merged 1 commit intomainfrom
fix/build-turbo-accounting
Apr 26, 2026
Merged

fix(build): turbo runner now actually reports per-task accounting (#830)#833
horza-bora merged 1 commit intomainfrom
fix/build-turbo-accounting

Conversation

@Dave-London
Copy link
Copy Markdown
Owner

Summary

Closes #830.

mcp__pare-build__turbo was returning {success: true, totalTasks: 1, passed: 0, failed: 0, cached: 0, tasks: []} on every modern (turbo 2.x) run — claiming success while reporting zero per-task data.

Root cause

Turbo 2.x emits per-task status lines as <pkg>:<task>: cache hit/miss/bypass ... (colon separator, no trailing (duration)). The parser regex TURBO_TASK_RE was hard-coded to ^(.+?)#(\S+):\s+cache\s+(hit|miss|bypass)(?:,\s+\w+[^(]*)?\(([^)]+)\) — requiring # between package and task and a mandatory (duration) suffix. Every per-task line silently failed to match, so tasks: [] while totalTasks came from the Tasks: N successful, M total summary line. Failure parsing was broken in the same way.

The 1.9s duration mentioned in the issue is turbo actually running — turbo just refused to print extra accounting because the run completed in milliseconds; the parse failure happened on the legitimate output.

Fix

packages/server-build/src/lib/parsers.ts:

  • TURBO_TASK_RE now accepts both : and # between package and task, and treats the trailing (duration) as optional.
  • New TURBO_ERROR_LINE_RE / TURBO_FAILED_LIST_RE patterns parse turbo 2.x's failure summary lines ( ERROR pkg#task: ... exited (N) and Failed: pkg#task).
  • Small upsert helper so a later failure line for the same task overrides an earlier cache bypass "pass" classification.
  • When the Tasks: N successful, M total summary line is present, it is trusted as ground truth for totalTasks / passed / failed (failed = M − N) — preserving the passed + failed === totalTasks invariant even if per-task lines drift in a future turbo release.

Before / after

Manual repro against the captured turbo 2.9.6 output for --filter @paretools/shared --force:

Before:

{ "success": true, "duration": 1.3, "tasks": [], "totalTasks": 1, "passed": 0, "failed": 0, "cached": 0 }

After:

{
  "success": true,
  "duration": 1.3,
  "tasks": [
    { "package": "@paretools/shared", "task": "build", "status": "pass", "cache": "miss" }
  ],
  "totalTasks": 1, "passed": 1, "failed": 0, "cached": 0
}

Failure case (deliberate broken TS file under --force):

{
  "success": false, "duration": 1.4,
  "tasks": [ { "package": "@paretools/shared", "task": "build", "status": "fail", "cache": "miss" } ],
  "totalTasks": 1, "passed": 0, "failed": 1, "cached": 0
}

Test plan

  • 12 existing turbo parser/formatter tests still pass (no regressions on legacy # fixtures)
  • 5 new regression tests for modern turbo 2.x output:
    • cache bypass on --force
    • cache hit (replaying logs)
    • cache miss (no inline duration)
    • failing build via ERROR + Failed: lines
    • invariant check across all 10 fixtures: passed + failed === totalTasks
  • Full @paretools/build suite green (262/262)
  • pnpm format:check clean on touched files
  • ESLint clean on touched files
  • Manual repro against real turbo 2.9.6 output via the rebuilt parser confirms correct accounting

Changeset

patch to @paretools/build.

…sk accounting (#830)

Turbo 2.x emits per-task status lines as "<pkg>:<task>: cache hit/miss/bypass ..."
(colon separator, no trailing duration in parens). The parser was hard-coded to
match the legacy "<pkg>#<task>:" form with a mandatory "(duration)" suffix, so
every task line was silently dropped — leaving "tasks: []", "passed: 0",
"failed: 0", "cached: 0" while "totalTasks" came from the "Tasks: N successful,
M total" summary line.

Changes:
- TURBO_TASK_RE now accepts both ":" and "#" between package and task name and
  treats the trailing "(duration)" as optional.
- New TURBO_ERROR_LINE_RE / TURBO_FAILED_LIST_RE patterns parse turbo 2.x's
  failure summary lines (" ERROR  pkg#task: ... exited (N)" and
  "Failed:    pkg#task").
- Failure information always wins over an earlier "cache bypass" pass line for
  the same task via a small upsert helper.
- When the "Tasks: N successful, M total" summary line is present we trust it
  as ground truth for totalTasks/passed/failed (failed = M - N), preserving
  the "passed + failed === totalTasks" invariant even if per-task lines drift
  in future turbo releases.

Adds 5 regression tests covering modern turbo 2.x output: cache bypass on
force, cache hit, cache miss, ERROR+Failed failure summaries, and an invariant
check across all fixtures.
@horza-bora horza-bora merged commit f37c6ef into main Apr 26, 2026
11 checks passed
@horza-bora horza-bora deleted the fix/build-turbo-accounting branch April 26, 2026 06:39
@pare-ci-bot pare-ci-bot Bot mentioned this pull request Apr 26, 2026
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.

pare-build/turbo: returns success with totalTasks: N but passed/failed/cached all 0 and empty tasks: []

2 participants