refactor(2/12): add pipeline event types, parsers, and event builders#320
Merged
cameroncooke merged 15 commits intomainfrom Apr 9, 2026
Merged
refactor(2/12): add pipeline event types, parsers, and event builders#320cameroncooke merged 15 commits intomainfrom
cameroncooke merged 15 commits intomainfrom
Conversation
585faf0 to
9a660e0
Compare
7a6f581 to
477dab1
Compare
a3f99ae to
1e526e6
Compare
The lastIssueDiagnostic type was missing suiteName, causing it to be silently dropped when parsing Swift Testing issue lines. The emitted test-failure event now correctly includes the suite field.
1e526e6 to
255ecb4
Compare
Collaborator
Author
This was referenced Apr 9, 2026
3 tasks
The >= 3 slash-part branch hardcoded indices 0, 1, 2 which dropped parts beyond the third. Swift Testing supports nested suites so names like Module/OuterSuite/InnerSuite/testMethod are possible. Now uses slice/at to preserve the full suite path and final test name.
commit: |
… leaks The pendingFailureDurations map was never cleaned up after a duration was consumed, allowing stale entries to attach incorrect durations to later failures with the same suite/test key in retry scenarios. Also adds a clarifying comment on the Swift Testing issue-count-as- failed-count approximation.
Fixes discovered by auditing synthetic test data against real swift test and xcodebuild output: - Swift Testing verbose mode: (aka 'func()') suffix now optionally matched in result and issue parsers - Skipped test format: real output uses arrow symbol and bare function name, not diamond with quoted name. Both formats now handled. - Parameterized tests: 'with N test cases' suffix in results and 'with N argument value' in issues now matched - Build errors without line numbers: .xcodeproj path-based errors like 'Missing package product' now parsed - Stage detection: lowercase 'Test suite' from Xcode 26 now matched Tests updated to use patterns captured from real swift test and xcodebuild runs.
…ting failure duration - Deep-copy arrays in snapshot() and finalize() so returned state is truly immutable and won't be mutated by subsequent push() calls - Replace overly broad noise regex that matched any 'identifier: content' line (swallowing compiler note: diagnostics) with a targeted pattern that only matches SPM resolved dependency lines (PackageName: https://...) - Attach failure duration from Swift Testing result lines by checking the result line before flushing the pending issue diagnostic. Previously all Swift Testing failure durations were silently dropped.
…e count Two bugs reproduced: - Test failure dedup key uses only location|message, collapsing distinct failures from different tests sharing the same assertion line - Parameterized test results drop the case count, undercounting progress
- Test failure dedup key now includes the test name when a location is present, preventing distinct failures from different tests sharing the same assertion line from being collapsed. Suite name is excluded from the location-present key because it disagrees between xcresult and live parsing sources. - ParsedTestCase gains a caseCount field populated from the 'with N test cases' suffix in Swift Testing parameterized results. Event parsers increment progress by caseCount instead of 1.
The issue regex used [^:]*? to match argument values before 'at', which failed when argument values contained colons (e.g. key:value). Changed to .*? and rely on the file:line:col anchor to correctly backtrack. Also fixes prettier formatting from previous commit.
recordTestCaseResult in xcodebuild-event-parser hardcoded += 1 for all count increments, ignoring the caseCount field from parameterized Swift Testing results. Also made the XCTest fallback path in the Swift Testing event parser consistent.
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ce10d30. Configure here.
…ationText on ParsedTotals The Resolve Package Graph and Resolved source packages noise patterns were unreachable dead code -- resolveStageFromLine matches them first and returns a build-stage event. Renamed ParsedTotals.durationText to displayDurationText to make clear it's a display string not parseable by parseDurationMs (the XCTest totals format is '1.234 (1.235) seconds' which doesn't parse).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
This is PR 2 of 12 in a stacked PR series that decouples the rendering pipeline from MCP transport. Depends on PR 1 (logging removal). This PR is additive -- it introduces new modules with no changes to existing code.
Introduces the foundational type system and parsing infrastructure for the new rendering pipeline. The core idea: tools communicate their results through structured
PipelineEventobjects rather than constructing MCPToolResponsecontent directly. This decouples what a tool wants to say from how it gets rendered.Architecture
The pipeline event model has three layers:
Event types (
src/types/pipeline-events.ts): A discriminated union of all event kinds --header,status-line,detail-tree,diagnostic-summary,next-steps,progress, etc. Each event is a plain data object with no rendering logic.Parsers that produce events from xcodebuild/Swift Testing output:
xcodebuild-event-parser.ts: Converts raw xcodebuild stdout lines into structured events (build progress, warnings, errors, test results)xcodebuild-line-parsers.ts: Low-level regex-based line classifiers for xcodebuild outputswift-testing-event-parser.ts: Handles Swift Testing's JSON event stream formatswift-testing-line-parsers.ts: Line classifiers for Swift Testing console outputxcodebuild-run-state.ts: Stateful tracker that deduplicates and orders events during a build/test runxcodebuild-error-utils.ts: Error extraction helpersEvent builders (
tool-event-builders.ts): Factory functions (header(),statusLine(),detailTree(), etc.) that construct properly-typed events. These are what tool handlers call.Why this comes first
Every subsequent PR in the stack depends on these types. The parsers are used by the xcodebuild pipeline (PR 4), the event builders are used by every tool handler (PRs 6-9), and the event types flow through the rendering engine (PR 3) and runtime invoker (PR 5).
Stack navigation
Test plan
npx vitest runpasses -- new test files for each parser module