Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: DataDog/dd-sdk-swift-testing
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2.7.4
Choose a base ref
...
head repository: DataDog/dd-sdk-swift-testing
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2.7.5
Choose a head ref
  • 19 commits
  • 132 files changed
  • 3 contributors

Commits on Jun 1, 2026

  1. Merge pull request #266 from DataDog/yehor.popovych/sdtest-3772-telem…

    …etry-exporter
    
    [SDTEST-3772] Implement TelemetryExporter
    ypopovych authored Jun 1, 2026
    Configuration menu
    Copy the full SHA
    4c0c7fd View commit details
    Browse the repository at this point in the history
  2. [SDTEST-3773] Add TelemetryLogExporter bridging OTel logs to Telemetr…

    …yExporter (#267)
    
    Implements LogRecordExporter that adapts ReadableLogRecord to TelemetryLog
    and forwards batches to TelemetryExporter for storage and upload.
    
    Co-authored-by: Claude Sonnet 4.6 <[email protected]>
    ypopovych and claude authored Jun 1, 2026
    Configuration menu
    Copy the full SHA
    003d90c View commit details
    Browse the repository at this point in the history

Commits on Jun 2, 2026

  1. Remove unused EncodableValue helper (#270)

    Drop `EncodableValue` and its test helpers (`Encoding.swift`,
    `EncodableValueTests.swift`), updating `JSONEncoderTests` and the Xcode
    project accordingly.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
    ypopovych and claude authored Jun 2, 2026
    Configuration menu
    Copy the full SHA
    9c21331 View commit details
    Browse the repository at this point in the history
  2. [SDTEST-3774] Add TelemetryMetricExporter bridging OTel metrics to Te…

    …lemetryExporter (#268)
    
    * [SDTEST-3774] Add TelemetryMetricExporter bridging OTel metrics to TelemetryExporter
    
    Implement MetricExporter that converts OTel MetricData to TelemetryMetric
    and forwards to TelemetryExporter for batching and upload, following the
    same pattern as TelemetryLogExporter.
    
    - Gauge/Sum types map to gauge or count series
    - Histogram/ExponentialHistogram/Summary emit .count and .sum sub-series
    - Attributes are converted to sorted tag arrays
    - getAggregationTemporality returns cumulative for all instrument types
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Add TelemetryLogExporterTests
    
    Covers message extraction (body, message attribute, eventName, default),
    severity→level mapping (error/fatal→ERROR, warn→WARN, others→DEBUG),
    stack trace extraction, attribute→tag conversion, timestamp fallback,
    multiple records, and forceFlush.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Fix DataUploadWorker.flush() returning false when queue is empty
    
    An empty flush (no pending batches) is a success — there was nothing to
    upload, so the operation trivially succeeded. Initialising result=true
    instead of false makes flush() consistent with that expectation and
    allows forceFlush()/flush() callers to distinguish "nothing to do" from
    a real upload failure.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Add TelemetryDistribution type for the distributions telemetry request
    
    Implements the distributions payload type per the telemetry spec:
    - TelemetryDistribution with its own Namespace (tracers/profilers/rum/appsec)
      and Series carrying raw sample values ([Double]) rather than [timestamp,value] pairs
    - Wired into TelemetryRequestType enum, TelemetryMessageBatch decoder, and TelemetryApi
    - sendDistributions() added to TelemetryApi protocol and TelemetryApiService
    - TelemetryExporterTests extended to cover distributions round-trip and mixed-type batches
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Use distributions for OTel histogram and exponential-histogram metrics
    
    Histogram types carry sampled data, so they map naturally to the
    distributions request type rather than the generate-metrics count/sum pair.
    
    Conversion strategy:
    - Histogram (explicit boundaries): reconstruct approximate sample values
      using bucket midpoints; edge buckets use min/max when available.
    - ExponentialHistogram: geometric midpoint of each exponential bucket
      (base^(offset+i+0.5) where base = 2^(2^-scale)).
    - Summary: kept as generate-metrics count+sum (already pre-aggregated).
    
    TelemetryMetricExporter.export(metrics:) now emits a TelemetryMetric
    payload for scalar types and a separate TelemetryDistribution payload for
    histogram types when both are present in the same batch.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Fall back to boundary edge for histogram underflow bucket without min
    
    When a histogram's underflow (first) bucket has no recorded min, use the
    first explicit boundary as the representative value instead of 0. A value
    in the underflow bucket is <= boundaries[0], so the boundary itself is a
    closer approximation than 0 — especially for histograms whose boundaries
    don't start near zero.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Request delta temporality for counters and histograms
    
    The exporter previously declared cumulative temporality for all instruments.
    Under cumulative, OTel emits running totals since process start on every
    collection, which is wrong for the DD targets:
    - DD `count` sums submitted values per interval, so feeding it cumulative
      snapshots inflates the count.
    - `distributions` expect the interval's raw samples, so reconstructing from
      cumulative bucket counts re-emits the entire sample set each cycle.
    
    Mirror OTel's deltaPreferred() selector: up/down counters (mapped to DD
    gauge) stay cumulative so the current running total is reported; counters,
    histograms, and gauges request delta.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Allow per-metric namespace via the OTel Resource
    
    Read the telemetry namespace from each metric's Resource (attribute key
    "dd.telemetry.namespace") and set it as the per-series namespace override.
    This lets a meter provider route its metrics to a specific namespace without
    a dedicated exporter instance. When the attribute is absent or its value
    isn't valid for the target payload (e.g. a generate-metrics-only namespace
    on a distribution), the override is omitted and the exporter's configured
    payload-level namespace applies.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Expose telemetry namespace as a Resource accessor
    
    Replace the ad-hoc TelemetryMetricResourceKeys constant and MetricConversion
    namespace helpers with a typed `Resource.telemetryNamespace` getter/setter in
    OpenTelemetry+Extensions, matching the existing applicationName/environment/etc.
    accessors. The converter now reads metric.resource.telemetryNamespace and maps
    it to the payload-specific Namespace enum via init(rawValue:).
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Use typed, per-payload namespace accessors on Resource
    
    Split the single string-based telemetryNamespace into two typed accessors,
    one per payload type with its own resource key:
    - telemetryMetricNamespace: TelemetryMetric.Namespace?
      (key "dd.telemetry.metric.namespace")
    - telemetryDistributionNamespace: TelemetryDistribution.Namespace?
      (key "dd.telemetry.distribution.namespace")
    
    Getters/setters work directly with the enum types (no String at the call
    site), and the separate keys let metrics and distributions carry independent
    namespaces — necessary since the two enums accept different value sets.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Sonnet 4.6 <[email protected]>
    ypopovych and claude authored Jun 2, 2026
    Configuration menu
    Copy the full SHA
    ee8887b View commit details
    Browse the repository at this point in the history

Commits on Jun 3, 2026

  1. Trim span tags, metadata and log attribute values to backend 5000-cha…

    …r limit (#269)
    
    The backend accepts at most 5000 characters per tag for spans and span
    metadata. Add the limit as `AttributesSanitizer.Constraints.maxAttributeValueLength`
    (the common sanitizer for all features) and enforce it across:
    
    - Span tags: trim values via `AttributesSanitizer.sanitizeValues`, reused by
      both `DDSpan` (`SpanSanitizer`) and `TestSpan`. Non-numeric values are
      serialized to their `description` (the representation the encoders emit) and
      truncated; numeric values pass through as metrics.
    - Span metadata: trim keys and string values via `SpanMetadata.trimmed(toMaxLength:)`,
      applied when the file header is built/updated in `SpansExporter`.
    - Log attribute values: trim string values in `LogSanitizer`, referencing the
      shared constant.
    - `TestError` crash-log splitting now uses the shared constant instead of a
      hardcoded 5000.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
    ypopovych and claude authored Jun 3, 2026
    Configuration menu
    Copy the full SHA
    9b16daa View commit details
    Browse the repository at this point in the history
  2. [SDTEST-3775] Add common telemetry manager with counter/histogram ins…

    …tances (#271)
    
    * [SDTEST-3775] Add common telemetry manager with counter/histogram instances
    
    Introduce a `Telemetry` manager (DatadogSDKTesting) that owns a MeterProvider
    wired to the Datadog instrumentation-telemetry metric pipeline
    (PeriodicMetricReader -> TelemetryMetricExporter -> TelemetryExporter, under the
    `civisibility` namespace) and provides an instrument for every CI Visibility
    metric.
    
    Metrics are exposed as a typed, discoverable tree -
    `telemetry.metrics.git.command.add(command: .getRepository)` - where each
    metric's add()/record() names exactly the tags it accepts and tag values are
    typed enums/Bool/Int rendered via the existing `SpanAttributeConvertible`.
    The metric set is taken from the shared instrumentation-telemetry spec and
    cross-checked against dd-trace-go (59 metrics: 37 counters + 22 distributions).
    
    This only provides the instances; features do not record into them yet.
    
    - EventsExporter: make TelemetryMetricExporter / TelemetryLogExporter public;
      add `civisibility` to the metric/distribution namespace enums.
    - Config: add DD_INSTRUMENTATION_TELEMETRY_ENABLED (default true) and
      DD_TELEMETRY_HEARTBEAT_INTERVAL (seconds, clamped 1...3600, default 60).
    - SessionManager: build the Telemetry manager (gated by the enable flag) and
      thread it through the optional SessionConfig.telemetry; shut it down on stop.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Fix flaky testExportMultipleItems_batchedInOneFile_uploadedAsOneEnvelope
    
    The `appendToOneFile` storage mock used `minFileAgeForRead: -1`, making the
    still-open file immediately eligible for the periodic upload worker (firing
    every 0.05s). When the worker fired between the test's synchronous writes it
    uploaded and deleted the file after only some entries, splitting the batch
    across uploads and intermittently failing the 4-entry assertion.
    
    `flush()` drains via `getAllReadableFiles()`, which ignores `minFileAgeForRead`,
    so setting it to `.greatestFiniteMagnitude` keeps the periodic worker from ever
    racing the writes while flush still drains the whole file as one batch — exactly
    the behaviour under test.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
    ypopovych and claude authored Jun 3, 2026
    Configuration menu
    Copy the full SHA
    f35d42c View commit details
    Browse the repository at this point in the history
  3. [SDTEST-3776] Wire telemetry manager through the SDK to gather reques…

    …t/upload metrics (#272)
    
    * [SDTEST-3776] Wire telemetry manager through the SDK to gather request/upload metrics
    
    Introduce telemetry observer hooks in EventsExporter and wire the common
    `Telemetry` manager through the tracer so the network/storage layers and the
    API-calling features can record CI Visibility metrics.
    
    EventsExporter (producer side — telemetry-agnostic):
    - Add `RequestObserver` (HTTP request: duration, request/response bytes, status),
      `UploadObserver` (background batch lifecycle) and `PayloadObserver`
      (serialization time + event count) protocols.
    - Measure request facts once in `HTTPClient.perform`; thread an optional
      `observer` through every API method (with `@inlinable` no-observer
      conveniences) and the upload pipeline (`DataUploadWorker`, `FileWriter`).
    - Bundle them as `ExporterObservers` plumbed through `Exporter` to the spans /
      coverage sub-exporters.
    - Fix `*ApiService` to depend on `HTTPClientType` instead of the concrete
      `HTTPClient` (they bypassed the injectable protocol).
    
    DatadogSDKTesting (consumer side):
    - Create the `Telemetry` manager with the tracer (before the exporter) so it can
      observe the upload pipeline and reach the feature factories; expose it as
      `DDTracer.telemetry` and source `SessionConfig.telemetry` from it.
    - Add observer adapters mapping the neutral callbacks to `Telemetry.metrics.*`
      with a shared `statusCode -> error_type` derivation, plus per-family request
      observers.
    - Gather `endpoint_payload.*` (via the exporter observers) and the API-request
      families `git_requests.*` / `itr_skippable_tests.*` / `known_tests.*` /
      `test_management_tests.*` (via per-call request observers threaded into the
      git uploader, TIA / known-tests / test-management factories and the settings
      fetch).
    
    Reuse a single `ExporterConfiguration` for the exporter and telemetry, now on
    the default performance preset.
    
    Response item counts and the local feature metrics (events, git commands, ITR
    decisions, code coverage) remain for SDTEST-3777.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Add telemetry architecture doc for SDTEST-3777 handoff
    
    Describes the metric tree (3775), the observer-hook wiring (3776), what is
    gathered today vs. the remaining work for SDTEST-3777 (response item counts,
    endpoint_payload.dropped, local feature metrics), the cross-module rationale,
    lifecycle, gotchas and a file map.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
    ypopovych and claude authored Jun 3, 2026
    Configuration menu
    Copy the full SHA
    e9db1c7 View commit details
    Browse the repository at this point in the history
  4. Report endpoint_payload.dropped when the orchestrator removes a store…

    …d payload (#273)
    
    `FilesOrchestrator` silently deletes stored batches that are never uploaded —
    too old (`maxFileAgeForRead`) or purged to keep the directory under
    `maxDirectorySize`. Add an `onDrop` callback (invoked with the file's byte size,
    outside the state lock) wired to `UploadObserver.uploadDropped`, so those
    removals are counted as `endpoint_payload.dropped`. Successful upload deletions
    (`delete(readableFile:)`) are not reported. Wired for spans and coverage.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
    ypopovych and claude authored Jun 3, 2026
    Configuration menu
    Copy the full SHA
    633388a View commit details
    Browse the repository at this point in the history

Commits on Jun 5, 2026

  1. [SDTEST-3777] Gather remaining CI Visibility instrumentation telemetr…

    …y metrics (#274)
    
    * [SDTEST-3777] Gather remaining CI Visibility instrumentation telemetry metrics
    
    - event_created/event_finished/test_session via TelemetryEventsFeature (fires at
      start hooks matching dd-trace-go; reads CI provider from SessionConfig.env)
    - events.enqueuedForSerialization via PayloadObserver.eventEnqueued() per write
    - events.manualApiEvents in @objc manual-API entry points
    - git.command/command_ms/command_errors via GitUploader.recordedGitCommand helper
      (real exit codes via public Spawn.RunError.exitCode; 128+signal convention)
    - git_requests.objects_pack_files, settings_response (with impacted_tests_enabled
      from JSON), response counts for TIA/known-tests/test-management
    - itr.skipped/unskippable/forced_run in TestImpactAnalysis hooks
    - code_coverage.started/finished/is_empty/errors/files through the coverage stack
    - error_type granularity: RequestObserver carries transportError so URLError.timedOut
      maps to .timeout instead of .network
    - App-lifecycle protocol: app-started (direct, with SDK config snapshot), heartbeat
      timer, app-closing batched before final flush
    - SessionConfig now holds env: Environment and config: Config; removed duplicate
      platform/service/metrics properties; TestSession/Module/Suite protocols gained
      var configuration so features access CI/git without touching DDTestMonitor
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * [SDTEST-3777] Add git.commit_sha_match / git.commit_sha_discrepancy metrics
    
    Detects commit-SHA and repository-URL discrepancies between the three git
    info providers (ci_provider, local_git, user_supplied) while assembling
    Environment.git. Discrepancies are stored directly on the Git struct as
    [ShaDiscrepancy] so callers access env.git.discrepancies / shaMatched
    without a separate wrapper type.
    
    Emitted once per session start via TelemetryEventsFeature (auto path) and
    DDSession.start() (manual path).
    
    Also moves FileWriter.eventEnqueued() before encoder.encode() so it fires
    at the point an event enters the write queue rather than inside the file
    orchestrator closure.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Move git SHA check emission to TestSession extension
    
    Extract emitGitShaCheck(to:) into a TestSession extension so both the
    auto-instrumented path (TelemetryEventsFeature.testSessionWillStart) and
    the manual API path (DDSession.start) share a single call site.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Fold TestModule/TestSuite configuration into protocol-extension defaults
    
    The configuration accessor on modules and suites just walked up the
    parent chain (session.configuration / module.configuration), duplicating
    a value already reachable via the existing session/module accessors.
    Drop the per-type declarations and the TestModule/TestSuite protocol
    requirements in favor of default implementations, mirroring the existing
    `session` convenience extensions. Only TestSession keeps configuration as
    a real (stored) requirement. Applied the same cleanup to the test mocks.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Fix test compilation errors after Telemetry and SessionConfig changes
    
    - Telemetry: make telemetryExporter optional so the new test-only init
      (testMetricExporter:resource:exportInterval:) can skip lifecycle
      (app-started, heartbeat, app-closing) without needing a real
      TelemetryExporter or TelemetryApi
    - TelemetryTests: use new test init; add missing transportError: nil to
      requestFinished calls
    - SwiftTestingTraitTests: update SessionConfig construction to the new
      (env:config:) signature, removing the removed platform/service/metrics
      parameters
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Fix FileWriterTests: add eventEnqueued() to RecordingPayloadObserver
    
    PayloadObserver gained eventEnqueued() when the per-event enqueue hook
    was added; update the test spy to conform.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Fix NTPClock crash and move Telemetry test init to test code
    
    Add SyncingClock wrapper: wraps any Clock and replaces the inner clock
    with DateClock on sync failure (via Synced<any Clock>). `now` always
    works — no crash regardless of sync state. Removes all DDTestMonitor.clock
    = DateClock() replacement sites.
    
    DDTestMonitor.clock is now always a SyncingClock. The convenience init in
    DDTracer calls sync() before creating the API service so the date provider
    is ready when app-started fires.
    
    Remove the test-only init from production Telemetry.swift. Move it to
    TelemetryTests.swift as a convenience init extension that delegates to
    the production init using a NoopTelemetryApi stub.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Fix testSettingsService_succeedsOnValidResponse: add impacted_tests_enabled to mock response
    
    The validSettingsBody helper was missing the impacted_tests_enabled field
    added to SettingsResponse; JSON decoding now requires it, so the test was
    failing with a keyNotFound error wrapped as a communication error.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * better clock types
    
    * Drop redundant config parameter from manager observer protocols
    
    TestSessionManagerObserver and TestModuleManagerObserver took the config
    alongside the session/module, but the model now exposes it directly via
    session.configuration / module.configuration. Drop the `with config:`
    parameter from all six methods and read it off the model inside the only
    real conformer (SessionAndModuleObserver).
    
    This also removes the now-unused stored `config` from DDModule's
    Stateless/StatefulManager and their initializers, since they only held it
    to forward to the observer.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Collapse TestSessionManager session/config tuple into a single session
    
    The manager exposed session, config, and a sessionAndConfig tuple, but
    every consumer split the tuple and stored config separately even though
    the session now carries it via session.configuration. Reduce the protocol
    to a single `session` getter and drop config/sessionAndConfig plus the
    derivation extension.
    
    Removes the now-redundant duplicate config storage that rode alongside
    the model in DDXCTestObserver.ModuleContext, SwiftTestingContext's
    ModuleContext and the suite factory, and SwiftTestingSuiteContext (whose
    configuration is now derived from suite.configuration). Callers read
    config off the model instead.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Sonnet 4.6 <[email protected]>
    ypopovych and claude authored Jun 5, 2026
    Configuration menu
    Copy the full SHA
    ad6793e View commit details
    Browse the repository at this point in the history

Commits on Jun 8, 2026

  1. Replace OTel metrics SDK with a custom telemetry collector (#275)

    * Replace OTel metrics SDK with a custom telemetry collector
    
    Telemetry distributions are sent to the DD intake as raw sample arrays so the
    backend computes the summary. The OTel-histogram path bucketed samples and then
    reconstructed approximate values from bucket midpoints on export — lossy
    (distorts percentiles) and complex (separate explicit/exponential code paths).
    
    Drop the OpenTelemetry metrics SDK (meter provider, views, periodic reader, the
    TelemetryMetricExporter bridge) in favor of a small lock-protected MetricStore:
    counters accumulate a per-interval delta, distributions keep raw samples, and
    each drain takes-and-resets the buffers (delta semantics). The store is drained
    to the durable TelemetryExporter via a new TelemetryPayloadExporter sink
    protocol.
    
    Two robustness refinements:
    - A short flush interval (10s) shrinks the window of in-memory metrics a crash
      could lose — export() persists to disk immediately and upload happens later on
      the exporter's own schedule, so this adds no network traffic.
    - When buffered distribution samples reach a cap, record() force-flushes so a
      burst is persisted rather than dropped (dd-trace-go drops on a full buffer).
    
    The metric tree and tag enums are unchanged; only the Counter/Distribution/
    Factory handle internals now target the store. Tests rewritten against an
    in-memory payload sink; ARCHITECTURE.md updated.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Make telemetry flush interval and distribution buffer size configurable
    
    Wire the two collector tunables to Config so they can be overridden by env vars,
    mirroring DD_TELEMETRY_HEARTBEAT_INTERVAL:
    - DD_TELEMETRY_FLUSH_INTERVAL (seconds, clamped 1...3600, default 10)
    - DD_TELEMETRY_DISTRIBUTION_BUFFER_SIZE (samples, clamped >= 1, default 2048)
    
    DDTracer threads them from Config into Telemetry; the hardcoded defaults remain
    as the fallback for direct construction.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Key telemetry metrics by a tag Set instead of a sorted array
    
    Set equality/hashing is intrinsically order-independent, so the metric key
    no longer relies on remembering to sort tags at record time, and duplicate
    "key:value" entries collapse. The wire array is sorted once per series at emit
    time for deterministic output. Matches dd-trace-go semantics (same name +
    different tags = separate series).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Guard MetricStore state with Synced instead of a bare UnfairLock
    
    Wraps the counters/distributions/sample-count in a single Synced<State>,
    matching the locking pattern used elsewhere in the SDK (e.g. DDModule) rather
    than hand-rolling an UnfairLock. Behavior is unchanged: take-and-reset on flush
    via `defer { s = State() }`.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Raise default distribution buffer cap to 65536
    
    The cap is a global safety backstop for bursts; the 10s timer does the normal
    draining. Samples are 8-byte Doubles, so a generous cap is cheap (65536 ≈ 512 KB)
    and keeps force-flush from triggering under normal CI telemetry volume.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Stamp telemetry metric points with the SDK clock, not Date()
    
    MetricStore now takes the NTP-synced SDK clock (DDTestMonitor.clock) and uses
    clock.now for count-series timestamps instead of a raw Date(), matching the
    rest of the SDK's time handling. Tests pass a DateClock().
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Remove unused telemetry namespace properties from Resource
    
    telemetryMetricNamespace / telemetryDistributionNamespace were only read by the
    deleted OTel TelemetryMetricExporter bridge. The custom collector stamps the
    .civisibility namespace directly on each payload, so these Resource accessors
    have no remaining readers.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Merge duplicated metric types in the telemetry tree
    
    Most metrics differed only in name, not shape: an untagged counter, an untagged
    distribution, or a counter tagged solely by error_type / event_type / endpoint.
    Replace the ~30 near-identical leaf structs with six reusable handle types
    (NoTagCounter, NoTagDistribution, ErrorTypeCounter, EventTypeCounter,
    EndpointCounter, EndpointDistribution) plus matching Factory helpers.
    
    Metrics with a unique tag signature (events.created/finished, session.started,
    git.command*, settings_response, requests_errors, code_coverage library
    counter) keep their own struct. Call sites and wire output are unchanged;
    TelemetryMetrics.swift drops from 682 to 513 lines.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Add missing impacted_tests_enabled to mock backend settings response
    
    #274 added a required `impactedTestsEnabled` field to the SDK's SettingsResponse
    model but didn't update the integration-test mock backend, so the settings
    response it returned no longer decoded — failing every integration test that
    boots the SDK against the mock.
    
    Emit `impacted_tests_enabled` from MockBackend.buildSettingsResponse (configurable
    via Settings.impactedTestsEnabled, default false). Matches the field set the SDK
    decoder requires, as covered by LibraryConfigurationServiceThrowTests.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Add telemetry integration coverage; flush telemetry synchronously at shutdown
    
    Verifying telemetry end-to-end surfaced a real bug: Telemetry.shutdown() drained
    the in-memory store to the exporter and enqueued app-closing, but only called
    exporter.shutdown() (stop the worker) — never exporter.flush(). Since flush() is
    what synchronously uploads, the final batch (self-metrics + app-closing) sat on
    disk unsent at process exit; only the direct app-started POST reliably reached
    the backend. Add exporter.flush() before shutdown so the last batch is uploaded.
    
    Tooling to assert telemetry reaches the backend:
    - MockBackend.parseTelemetry decodes the captured /api/v2/apmtelemetry bodies
      (both the direct single-payload form and the message-batch envelope) into typed
      TelemetrySeries + observed event types, exposed via Requests.telemetry* accessors.
    - MockBackendTelemetryParsingTests unit-tests the parser against the wire format.
    - A telemetryReported integration test asserts app-started/app-closing events and
      generate-metrics (test_session) round-trip to the backend over a basic run.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
    ypopovych and claude authored Jun 8, 2026
    Configuration menu
    Copy the full SHA
    083936c View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    117d696 View commit details
    Browse the repository at this point in the history
  3. Refactor SpanMetadata.allTest from array to single SpanType value (#276)

    Replace the loop over `SpanType.allTest: [Self]` with a single `"test_levels"` span type, allowing tag assignment to all test levels in one call instead of iterating over four types.
    
    Co-authored-by: Claude Sonnet 4.6 <[email protected]>
    ypopovych and claude authored Jun 8, 2026
    Configuration menu
    Copy the full SHA
    2cc3e85 View commit details
    Browse the repository at this point in the history

Commits on Jun 9, 2026

  1. Configuration menu
    Copy the full SHA
    796d43b View commit details
    Browse the repository at this point in the history

Commits on Jun 15, 2026

  1. [SDTEST-3830] CI Visibility instrumentation telemetry: metrics + logs (

    …#279)
    
    * [SDTEST-3830] Resolve correct product version for app/framework under test
    
    DDTracer read Bundle.main directly, which is the bare xctest runner for
    host-less test bundles, so it reported a wrong/unknown application version.
    
    Add Bundle.productUnderTest, a resolver that picks the bundle representing
    the product being tested and reports its version, with priority:
      1. DD_VERSION override (quick-exit: skips all discovery when set)
      2. Bundle.main when it is an .app (app-/UI-hosted tests)
      3. a dynamic framework named after XCODE_SCHEME_NAME, co-located with the
         .xctest bundle (sibling on macOS/simulator/device, or embedded), found
         among loaded frameworks or probed on disk (static frameworks ship in the
         products folder but are never loaded into memory)
      4. the .xctest bundle version (static/SPM-merged products)
      5. "<unknown>"
    
    The selection policy is a pure function over value-type descriptors so it is
    fully unit-tested. Adds DD_VERSION as a configuration key.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Detect Swift/runtime version via RuntimeInfo; recreate tracer on shutdown
    
    Replace the Mach-O LC_BUILD_VERSION compiler-version scanner (CompilerVersion.c/h)
    with RuntimeInfo: an Xcode/SPM enum exposing runtimeName, runtimeVersion and
    swiftVersion. It maps the XCTest bundle's DTXcode to a Swift version (Xcode 26+),
    falling back to a single `swiftc --version` spawn only when needed (avoids the
    subprocess on macOS for both Xcode and SPM). XCODE_SCHEME_NAME selects the case.
    
    Thread the runtimeName/runtimeVersion through APIServiceConfig and
    TestOptimizationApiService so TelemetryApplication reports them instead of
    hardcoding "swift".
    
    Make DDTestMonitor.tracer lazily recreatable: SessionManager.endSession now
    shuts the tracer down (which tears down the global OpenTelemetry provider), and
    DDTracer.deinit restores the default providers. The next session rebuilds a
    tracer that re-registers a live SDK provider, so spans stay SpanSdk instead of
    crashing the `as! SpanSdk` casts. The backing store is guarded by Synced to keep
    the lazy init/reset race-free.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Fix tracer-shutdown deadlock, network swizzling crash; refine device/runtime info
    
    - shutdownTracer(): clear the tracer slot under the lock but run shutdown()
      outside it. shutdown() flushes/logs/uploads and stdout/stderr capture routes
      back through DDTestMonitor.tracer, so holding the non-reentrant lock during
      shutdown deadlocked.
    - URLSessionInstrumentation: check URLSessionDelegate conformance via the ObjC
      runtime (InstrumentationUtils.classConforms, walking the superclass chain with
      class_conformsToProtocol) instead of a Swift `is` cast. The cast messages every
      class from objc_getClassList() and aborted on pathological internal classes
      (__NSGenericDeallocHandler, __NSAtom, __NSMessageBuilder).
    - EnvironmentTests: update to the refactored PlatformUtils API
      (getPlatformVersion) and assert runtime tags against getRuntimeInfo().
    - Device/Platform/RuntimeInfo refactor wiring (getDeviceInfo/getKernelInfo).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Tie tracer lifecycle to the monitor; make network instrumentation self-contained
    
    Move the tracer off the global lazy accessor and onto the DDTestMonitor
    instance: it's created in init and torn down with the monitor (stop() flushes
    and shuts it down; DDTracer.deinit restores the default OpenTelemetry
    providers). Removed the static `tracer` accessor entirely — callers use
    `DDTestMonitor.instance?.tracer`, so no tracer is created when no monitor is
    installed (this is what caused the accidental backend connection), and there's
    no lock to deadlock on during shutdown.
    
    Session-scoped span creators (DDSession/DDModule/DDSuite/DDTest) get the tracer
    from SessionConfig.tracer, captured from the monitor when the session
    bootstraps. Ambient callers (stdout/stderr capture, crash, XCUI, instrumentation
    control) use the optional instance accessor and no-op without a monitor.
    
    DDNetworkInstrumentation is now self-contained: injectHeaders / recordPayload /
    disableNetworkCallStack / enableNetworkCallStackSymbolicated are its own
    properties passed at init, and the URLSessionInstrumentationConfiguration
    callbacks capture self weakly to break a retain cycle (config -> closures ->
    self -> urlSessionInstrumentation -> config).
    
    DDCrashes.install takes the tracer and returns CrashInformation? instead of
    reaching into DDTestMonitor.instance; the monitor stores the result.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Move upload-outcome logging into DataUploadWorker
    
    Restore the upload logging path: DataUploadWorker logs each batch's
    success/retry/drop with a failure description (DataUploadStatus.failureDescription),
    threaded through FeatureStoreAndUpload and the feature exporters. Remove the
    now-redundant per-request logs in the API services and the orphaned
    clientLibraryVersionHeader call. Drop the obsolete DeviceTests.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Simplify telemetry: own metric instruments + log handler
    
    Move telemetry metrics and logs off OpenTelemetry into hand-written stores,
    mirroring the existing MetricStore rationale (the DD intake wants raw samples /
    minimal log values, not OTel-shaped data).
    
    Metrics: drop the Typed* prefix — Counter<Tags>/Distribution<Tags> are now the
    only instruments, each paired with a per-metric MetricTags type and a constrained
    extension exposing the typed add/record. Removes the parallel untyped Counter/
    Distribution/Factory and the one-off wrapper structs.
    
    Logs: add a LogStore + telemetry.logs.error/warn/debug handler on Telemetry,
    deduplicating identical logs into one entry with a count and draining on the
    shared flush timer as the `logs` request type. Tags reuse the metric rendering.
    Delete the now-unused OTel TelemetryLogExporter bridge and its test.
    
    Also send DD-Client-Library-Version on the telemetry request directly, and
    document both stores in ARCHITECTURE.md.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Log request body and response data in HTTPClient debug mode
    
    Capture the uncompressed request body before deflate and log it alongside the
    response. Render payloads as UTF-8 text (JSON in nearly all cases), falling back
    to a `<binary N bytes>` marker when the bytes aren't valid UTF-8.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Log request/response headers; capture only needed log fields
    
    Add request and response headers to the HTTPClient debug log. Capture just the
    url, headers, and uncompressed body into a small LogFields value up front (only
    when debug is on), so the completion handler no longer retains the full
    URLRequest for the request's duration.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Send telemetry metric point timestamps as integer seconds
    
    TelemetryMetric.Series.Point.timestamp is now Int64 unix seconds (rounded),
    matching the intake's expected format, with a `time:` convenience init that
    rounds a TimeInterval. MetricStore stamps points with the rounded clock value.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Redact API/application keys from logged headers
    
    Replace DD-API-KEY and DD-APPLICATION-KEY values with **** in HTTPClient debug
    header logs (names sourced from HTTPHeader.Field, matched case-insensitively).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Disable telemetry debug flag
    
    Flip debugTelemetry to false for release; backend debug processing was only
    needed while validating the telemetry pipeline.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Upload unit-test JUnit reports to Datadog
    
    The SDK can't instrument its own test run, so report unit-test results by
    uploading the JUnit reports xcbeautify produces. Give each scheme/platform a
    distinct report filename so the two unit-test schemes no longer overwrite one
    junit.xml, then upload build/reports via datadog-ci in the unit-tests workflow
    (runs on failure too; skips gracefully when DD_API_KEY is absent, e.g. fork PRs).
    
    Use `npx --yes @datadog/datadog-ci` instead of a global install, here and in the
    codeQL SARIF upload.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * [SDTEST-3830] Parse backtick-quoted test function names with spaces
    
    The function-name regex used `\S+` for Swift symbols, truncating a backtick-
    quoted name with spaces (e.g. `failing parameterized test`(_:)) at the first
    space, so the source-line parser missed the function. Match backtick-quoted
    segments as a unit, and split the module on the last dot before the first
    backtick so a dot inside a quoted name isn't taken as the module separator.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
    ypopovych and claude authored Jun 15, 2026
    Configuration menu
    Copy the full SHA
    a145074 View commit details
    Browse the repository at this point in the history
  2. Add granular timing logs to diagnose slow Install Test Monitor on sim…

    …ulators
    
    Add Log.measure() calls inside installTestMonitor() and DDTracer.init to
    break down where the ~50s setup time is spent on simulator platforms.
    
    New measurements:
    - DDTestMonitor init
    - resolveHostname (developerMachineHostName / hostname subprocess)
    - NTP clock sync (waitForAsync on FallbackClock.sync)
    - TestOptimizationApiService init
    - Exporter init
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    ypopovych and claude committed Jun 15, 2026
    Configuration menu
    Copy the full SHA
    f91c204 View commit details
    Browse the repository at this point in the history
  3. Fix intermittent tvOS crash enumerating Obj-C classes during URLSessi…

    …on instrumentation (#281)
    
    * Fix tvOS crash enumerating Obj-C classes during URLSession instrumentation
    
    injectInNSURLClasses() enumerated every registered Obj-C class via
    objc_getClassList() and called class_copyMethodList on each. That list can
    include classes whose superclass chain references an unrealized stub class
    (weak-linked classes from unloaded frameworks, or relative-method-list stub
    classes). class_copyMethodList realizes the class, realization walks into the
    stub superclass, and the runtime aborts with "Attempt to use unknown class".
    Newer runtimes (Xcode 26.4 / tvOS) ship more of these stubs, which is why it
    surfaced now.
    
    Replace the unused, prefix-based objc_getSafeClassList with a real safety
    filter that keeps only classes whose entire superclass chain is itself in the
    class list, and use it from injectInNSURLClasses. Reading class_getSuperclass
    does not realize the class, so the filter never touches an unknown class.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * small log fix
    
    * Fix intermittent class-enumeration crash; drop superclass-walk workaround
    
    Root cause: objc_getClassList(buf, n) writes at most n entries but returns the
    total number of registered classes. During app/test launch other threads load
    frameworks and register classes concurrently, so the second call can return a
    count larger than the buffer we sized from the first call. Iterating to that
    larger count read uninitialized memory and produced garbage "class" pointers,
    which crashed intermittently as "Attempt to use unknown class" / bad access the
    moment they were dereferenced. Clamp the loop to the allocated capacity.
    
    This was the actual bug, so the earlier superclass-chain safe-list workaround is
    no longer needed and is removed (it also made things worse by dereferencing the
    garbage pointers extra times). Keep the classConforms optimization, which skips
    class_copyMethodList for the thousands of classes that don't adopt
    URLSessionDelegate, and drop the unused objc_getSafeClassList helper and unused
    imports.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    * Grow-and-retry objc_getClassList so no concurrently-added class is dropped
    
    Instead of clamping to the initial buffer capacity (which would silently drop
    classes registered mid-scan, possibly missing real URLSession delegates), keep
    re-querying and growing the buffer until objc_getClassList's returned total fits
    what we allocated. This captures every class while still never reading past the
    buffer. Small headroom lets it settle in a single retry under typical concurrent
    framework loading.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
    ypopovych and claude authored Jun 15, 2026
    Configuration menu
    Copy the full SHA
    4f6f247 View commit details
    Browse the repository at this point in the history

Commits on Jun 16, 2026

  1. Propagate simulator device/OS tags from inner test spans to runner sp…

    …ans (#283)
    
    * Propagate simulator device/OS tags from inner test spans to runner spans
    
    The integration test runner always executes on macOS, so its DDTest spans
    were tagged with macOS device info even when the inner tests ran on a
    simulator (iOS, tvOS, watchOS, visionOS). After xcodebuild finishes, copy
    os.platform, os.architecture, os.version, device.name, and device.model
    from the first received span onto DDTest.current — but only when the inner
    platform is not macOS, where the runner tags are already correct.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Fix simulator device tag propagation: read from envelope metadata directly
    
    Device/OS tags are written into the envelope-level metadata under the
    "test_levels" key by the SDK. SpanEvent.extend only merges metadata keyed
    by span.type ("test", "span", etc.) and "*", so span.meta never contains
    os.platform — making the previous guard always fail and the whole block
    get skipped.
    
    Fix: read from envelope.metadata["test_levels"] directly. For each device
    tag key, prefer a value found in individual span meta (spans can override
    via set(tag:)), falling back to the envelope metadata value.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Fix span metadata merge: add test_levels priority, scope to test* spans only
    
    Merge priority (highest to lowest):
      span.meta > metadata[span.type] > metadata["test_levels"] > metadata["*"]
    
    metadata["test_levels"] carries device/OS tags shared across all test-level
    events. It is applied only to test* spans (.test, suiteEnd, moduleEnd,
    sessionEnd) — not to generic "span" events where it does not belong.
    
    With test_levels now correctly merged into extended spans, the runner's
    device tag propagation can read from allSpans.meta directly (reverted from
    the previous direct envelope.metadata["test_levels"] workaround).
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Sonnet 4.6 <[email protected]>
    ypopovych and claude authored Jun 16, 2026
    Configuration menu
    Copy the full SHA
    65b0cd5 View commit details
    Browse the repository at this point in the history

Commits on Jun 17, 2026

  1. Fix 40+ second simulator test setup delay (#282)

    * Add loadSourceCodeInfo and activeFeatures timing measurements
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Measure lag between log.measure start and closure execution
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Measure config and env static property init time in installTestMonitor
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Fix 16s simulator setup delay: move git fetch off the critical path
    
    Environment.init() called mergeHeadInfo() synchronously, which runs
    `git fetch --deepen=1` on simulators. On CI simulators this fetch
    times out (~16s), blocking the entire test setup.
    
    Fix: remove mergeHeadInfo from Environment.init() and schedule it on
    instrumentationWorkQueue instead. setupCrashHandler() already waits
    for this queue before the session span is created, so the commit
    author/message metadata is still available when needed.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Add queue-wait logs; move mergeHeadInfo to Environment.fetchMergeHeadInfo
    
    - Wrap all waitUntilAllOperationsAreFinished calls with Log.measure so
      queue drain time is visible in debug output
    - Add Environment.fetchMergeHeadInfo(log:) instance method that calls
      mergeHeadInfo and updates self.git, wrapped in Log.measure
    - Add DDTestMonitor.loadGitCommitInfo() that schedules fetchMergeHeadInfo
      on instrumentationWorkQueue — called from installTestMonitor, not gated
      by disableSourceLocation (commit metadata is unrelated to source location)
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Add granular timing logs inside Environment.init
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * start loading of merge head ASAP
    
    * Fix build: use local var to avoid capturing self before init completes
    
    Log.measure closures inside Environment.init that referenced the stored
    property `sourceRoot` caused "self captured before all members initialized".
    Introduce a local `sourceRootValue` binding and use it inside the closures.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Fix 26s GitHub CI reader delay: add depth limit to glob expansion
    
    getJobId() expands /**/_diag globs by recursively scanning
    /Users/runner/actions-runner/, which on a CI machine can be a huge
    directory tree. Added a maxWildcardDepth=3 cap to expandGlobSegments
    so the ** wildcard stops expanding after 3 levels — the _diag directory
    is always 1-2 levels deep, so this preserves full functionality.
    
    Also add per-reader timing to env-ciReaders loop.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Move git ops to gitUploadQueue with proper ordering
    
    - mergeHeadInfo moves from instrumentationWorkQueue to gitUploadQueue
      (first operation); reference saved in mergeHeadOperation
    - gitStatusUpToDate (git subprocess) moves inside the upload operation
      so it no longer blocks test startup
    - uploadOp gets a dependency on mergeHeadOperation so env.git is fully
      populated before the upload runs
    - updateTracerConfig: when ITR does not require git upload, wait only
      for mergeHeadOperation (not the full queue) to get commit metadata
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Optimise GithubCI job-ID scan: lazy glob walk with early exit
    
    Replace the collect-all-then-search approach in getJobId with a
    FileManager.searchGlob extension that short-circuits as soon as
    the first Worker_ log match is found.
    
    Key changes:
    - FileManagerExtensions.swift: new FileManager.searchGlob(_:maxWildcardDepth:body:)
      walks a glob pattern (supporting **) calling body on each matched
      directory; returns true and stops on the first body hit. ** with a
      tail only calls body on dirs matching the full subpath; ** as the
      last segment calls body on every dir in the subtree. Walking starts
      from the longest concrete prefix before the first **, not from /.
    - GithubCI.swift: getJobId iterates _diagnosticDirs lazily via
      searchGlob and returns as soon as a match is found, so specific
      dirs (no **) are checked before the broad ** patterns.
    - FileManagerExtensionsTests.swift + fixtures/glob/: 12 tests covering
      exact match, missing path, tail vs no-tail body-call semantics,
      early exit, depth capping, and scoped root.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Make searchGlob generic: body returns T? instead of Bool
    
    body returning nil means "keep going"; any non-nil value stops the
    walk and is returned directly, eliminating the need for callers to
    capture a mutable variable to carry the result.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    * Rename searchGlob closure param from body to matcher; adopt linter fixes
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Sonnet 4.6 <[email protected]>
    ypopovych and claude authored Jun 17, 2026
    Configuration menu
    Copy the full SHA
    0ccc4d2 View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    9a7ff7e View commit details
    Browse the repository at this point in the history
Loading