Skip to content

refactor(2/12): add pipeline event types, parsers, and event builders#320

Merged
cameroncooke merged 15 commits intomainfrom
refactor/pipeline-event-types
Apr 9, 2026
Merged

refactor(2/12): add pipeline event types, parsers, and event builders#320
cameroncooke merged 15 commits intomainfrom
refactor/pipeline-event-types

Conversation

@cameroncooke
Copy link
Copy Markdown
Collaborator

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 PipelineEvent objects rather than constructing MCP ToolResponse content directly. This decouples what a tool wants to say from how it gets rendered.

Architecture

The pipeline event model has three layers:

  1. 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.

  2. 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 output
    • swift-testing-event-parser.ts: Handles Swift Testing's JSON event stream format
    • swift-testing-line-parsers.ts: Line classifiers for Swift Testing console output
    • xcodebuild-run-state.ts: Stateful tracker that deduplicates and orders events during a build/test run
    • xcodebuild-error-utils.ts: Error extraction helpers
  3. Event 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

  • PR 1/12: Remove deprecated logging tools
  • PR 2/12 (this PR): Pipeline event types, parsers, and event builders
  • PR 3/12: Rendering engine and output formatting
  • PR 4/12: Build/test utility extraction, platform steps, xcodebuild pipeline
  • PR 5/12: Runtime handler contract and tool invoker
  • PR 6-9/12: Tool migrations (simulator, device/macOS, UI automation, remaining)
  • PR 10/12: CLI, daemon, and MCP server boundaries
  • PR 11-12/12: Config, docs, snapshot tests

Test plan

  • npx vitest run passes -- new test files for each parser module
  • Event type discriminated union is exhaustive (verified by TypeScript compiler)
  • Parser tests cover edge cases (malformed lines, empty input, interleaved output)

@cameroncooke cameroncooke force-pushed the refactor/remove-logging-tools branch from 585faf0 to 9a660e0 Compare April 8, 2026 21:29
@cameroncooke cameroncooke force-pushed the refactor/pipeline-event-types branch from 7a6f581 to 477dab1 Compare April 8, 2026 21:29
@cameroncooke cameroncooke force-pushed the refactor/pipeline-event-types branch from a3f99ae to 1e526e6 Compare April 9, 2026 07:59
@cameroncooke cameroncooke deleted the branch main April 9, 2026 08:41
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.
@cameroncooke cameroncooke reopened this Apr 9, 2026
@cameroncooke cameroncooke force-pushed the refactor/pipeline-event-types branch from 1e526e6 to 255ecb4 Compare April 9, 2026 08:45
@cameroncooke cameroncooke changed the base branch from refactor/remove-logging-tools to graphite-base/320 April 9, 2026 09:07
@cameroncooke cameroncooke changed the base branch from graphite-base/320 to main April 9, 2026 09:07
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.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 9, 2026

Open in StackBlitz

npm i https://pkg.pr.new/xcodebuildmcp@320

commit: 7c13ea9

… 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.
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ 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).
@cameroncooke cameroncooke merged commit 4cc895d into main Apr 9, 2026
12 checks passed
@cameroncooke cameroncooke deleted the refactor/pipeline-event-types branch April 9, 2026 17:30
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