You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Setup is where people fall off, and every failure mode looks identical from the outside: dotnet xping report says nothing useful and the developer has no way to find out why.
Today the only diagnostic is ReportCommand.ReportUnavailable, which prints one of two messages — "No Xping local store found" or "No runs recorded yet in <path>" — both followed by "Run your tests once with the Xping SDK installed, then try again." That advice is correct for exactly one of the causes below and useless for the rest:
The SDK package is installed but tracking was never turned on (no [assembly: TestFramework(...)], no [XpingTrack], no XpingTestBase), so runs were never recorded.
NUnit/MSTest wiring is present but the teardown is missing, so sessions are collected and never flushed (documented at docs/troubleshooting/common-issues.md §4).
XPING_ENABLED=false or XPING_MODE=Disabled in the shell that ran the tests.
The store resolved to the user-profile fallback instead of the repo root, because the checkout was read-only (LocalStorePathResolver.Resolve → GetProfileFallback). The developer is looking in .xping/ and the data is somewhere else entirely.
XPING_LOCAL_STORE is set and points elsewhere.
Runs exist but there are fewer than MinimumSessionsToReport (5), so the window is real and the findings list is legitimately empty.
Runs exist but all of them are older than DefaultWindowDays (14), so the default window is empty while the store is not.
The store holds several assemblies and auto-scoping picked the newest, so the suite the developer cares about isn't in the report.
Every one of these is mechanically detectable. None of them is currently reported.
Proposed Solution
Add xping doctor — a read-only command that inspects the store, the environment and (best-effort) the test projects, and prints one line per check with the fix inline.
$ dotnet xping doctor
Store
ok .xping resolved from repository root (/Users/x/repo/.xping)
ok writable
ok 47 runs · 3.1 MB · newest 4 minutes ago
warn 9 runs evicted by retention in the last 7 days (MaxRuns 50)
A window of 20 needs runs to survive; raise MaxRuns or run `xping report --runs 12`.
History
ok Checkout.Tests · 44 runs · 412 tests
warn Legacy.Tests · 3 runs · below the 5-run floor, no findings will be emitted
fail every run is older than 14 days; the default window is empty
Run your tests, or widen the window: `xping report --since 2026-06-01`.
SDK
ok CLI 1.0.0-rc.5
warn newest run recorded by SDK 1.0.0-rc.2
RetryMasked findings need rc.4 or later. Update Xping.Sdk.XUnit.
Configuration
warn XPING_SAMPLINGRATE=0.5 in this shell
Half of all executions go unrecorded; every rate in the report is wrong.
ok XPING_ENABLED unset (default: true)
ok local mode — no API key set, nothing is uploaded
Projects
ok SampleApp.XUnit — Xping.Sdk.XUnit + [assembly: TestFramework(...)]
fail SampleApp.NUnit — Xping.Sdk.NUnit referenced, no [XpingTrack] found
Add `[assembly: XpingTrack]`. See https://docs.xping.io/getting-started/quickstart-nunit.html
2 failed · 3 warnings · 8 ok
The checks
Store — resolution and health.
Check
Detail
Which resolution rule won
XPING_LOCAL_STORE / repo root (.git, *.sln, *.slnx) / profile fallback. Naming the branch is the whole point: the profile fallback is silent today and is the single most confusing outcome.
Writable
Reuse LocalStorePathResolver.IsWritable's probe.
Runs, size, age spread, per assembly
Same data xping where prints; call it, don't reimplement it.
Unreadable sessions
Already surfaced as WindowResult.UnreadableSessions; doctor should name the files.
Retention pressure
MaxRuns 50 / MaxBytes 50 MB / MaxAge 30d. Warn when eviction is outpacing the analysis window.
Store tracked by git
.xping/.gitignore (*) is written automatically now, but a store committed before that shipped stays tracked. git ls-files --error-unmatch .xping answers it.
History — why the report is empty or thin. This is the highest-value group.
Local history is empty even though the store isn't
SDK — version skew. TestSession.SdkVersion is recorded on every session, so doctor can compare what wrote the history against the CLI reading it, and name the findings a stale adapter cannot produce.
Configuration — read the XPING_* surface and report what will change the outcome: ENABLED, MODE, SAMPLINGRATE (< 1.0 means the rates are wrong), CAPTURESTACKTRACES (off degrades failure signatures, which weakens SharedFailure clustering and the "N failure modes" counts), LOCAL_STORE, NO_BANNER, and the half-configured cloud case — APIKEY without PROJECTID or the reverse.
Projects — best-effort static detection, grep-level, warnings only. For each test project found near the store root: which adapter package is referenced, and whether the required wiring marker is present.
Read-only. Never writes, never mutates the store. A --fix mode is a separate decision, not this issue.
No network, ever, by default. The local path promises no network calls without an API key, and doctor must not be the command that breaks it. Any cloud connectivity probe to upload.xping.io goes behind an explicit --check-cloud flag.
--format json alongside the rendered form, same envelope discipline as report — versioned, stdout only, warnings to stderr.
Honours --directory, --ascii, --no-color, NO_COLOR exactly as report does, resolved through OutputCapabilities.Resolve.
Exit codes, consistent with report: 0 no failures (warnings allowed) · 1 at least one fail · 2 doctor itself could not run.
report points at it.ReportUnavailable's generic "Run your tests once with the Xping SDK installed" is replaced by "Run xping doctor to find out why."
Known constraint: the CLI's environment is not the test host's
doctor reads the XPING_* variables of its own process. The developer may have run dotnet test from a different shell, from an IDE test runner with its own environment, or in CI. Configuration findings must therefore be phrased as "in this shell" and, wherever possible, cross-checked against what the sessions actually recorded — LocalSessionProperties.Mode travels on every session and is authoritative about the mode the run really used. Where the two disagree, that disagreement is itself the most useful thing doctor can print.
Out of scope
--fix / interactive repair.
Cloud connectivity diagnosis beyond the opt-in reachability probe.
Replacing xping where — it stays, and doctor reuses its data.
Open questions
Should doctor run automatically when report exits 2, or only be pointed at? Pointing is safer: a diagnostic that runs itself unbidden is how a tool gets noisy.
How hard should project detection try? Walking every *.csproj under the repo root is cheap but noisy in a large solution; restricting to projects that have written sessions misses precisely the ones that never worked, which are the interesting case.
Does the retention-pressure check need real eviction history, which is not recorded today, or can it be inferred from the age/count spread alone?
Problem
Setup is where people fall off, and every failure mode looks identical from the outside:
dotnet xping reportsays nothing useful and the developer has no way to find out why.Today the only diagnostic is
ReportCommand.ReportUnavailable, which prints one of two messages — "No Xping local store found" or "No runs recorded yet in<path>" — both followed by "Run your tests once with the Xping SDK installed, then try again." That advice is correct for exactly one of the causes below and useless for the rest:[assembly: TestFramework(...)], no[XpingTrack], noXpingTestBase), so runs were never recorded.docs/troubleshooting/common-issues.md§4).XPING_ENABLED=falseorXPING_MODE=Disabledin the shell that ran the tests.LocalStorePathResolver.Resolve→GetProfileFallback). The developer is looking in.xping/and the data is somewhere else entirely.XPING_LOCAL_STOREis set and points elsewhere.MinimumSessionsToReport(5), so the window is real and the findings list is legitimately empty.DefaultWindowDays(14), so the default window is empty while the store is not.RetryMaskedis silent no matter how flaky the suite is.Every one of these is mechanically detectable. None of them is currently reported.
Proposed Solution
Add
xping doctor— a read-only command that inspects the store, the environment and (best-effort) the test projects, and prints one line per check with the fix inline.The checks
Store — resolution and health.
XPING_LOCAL_STORE/ repo root (.git,*.sln,*.slnx) / profile fallback. Naming the branch is the whole point: the profile fallback is silent today and is the single most confusing outcome.LocalStorePathResolver.IsWritable's probe.xping whereprints; call it, don't reimplement it.WindowResult.UnreadableSessions; doctor should name the files.MaxRuns50 /MaxBytes50 MB /MaxAge30d. Warn when eviction is outpacing the analysis window..xping/.gitignore(*) is written automatically now, but a store committed before that shipped stays tracked.git ls-files --error-unmatch .xpinganswers it.History — why the report is empty or thin. This is the highest-value group.
LocalAnalysisConstants.MinimumSessionsToReport(5)MinimumExecutionsToReport(5) — already counted asAnalysisResult.ExcludedLowEvidence, never explainedDefaultWindowDays(14) /DefaultWindowSessions(20)SDK — version skew.
TestSession.SdkVersionis recorded on every session, so doctor can compare what wrote the history against the CLI reading it, and name the findings a stale adapter cannot produce.Configuration — read the
XPING_*surface and report what will change the outcome:ENABLED,MODE,SAMPLINGRATE(< 1.0 means the rates are wrong),CAPTURESTACKTRACES(off degrades failure signatures, which weakensSharedFailureclustering and the "N failure modes" counts),LOCAL_STORE,NO_BANNER, and the half-configured cloud case —APIKEYwithoutPROJECTIDor the reverse.Projects — best-effort static detection, grep-level, warnings only. For each test project found near the store root: which adapter package is referenced, and whether the required wiring marker is present.
[assembly: TestFramework("Xping.Sdk.XUnit.XpingTestFramework", "Xping.Sdk.XUnit")][XpingTrack], plusXpingContext.Initialize()andFinalizeAsync/ShutdownAsyncin a[SetUpFixture]XpingTestBase, plus[AssemblyInitialize]/[AssemblyCleanup]; also warn on[assembly: Parallelize(Scope = MethodLevel)], which silently loses sessions (MSTest: sessions silently lost with [assembly: Parallelize(Scope = MethodLevel)] — [AssemblyCleanup] never runs, nothing is flushed or uploaded #124, follow-up MSTest: #124's process-exit safety net races vstest teardown — finalize pipeline often never completes #126)Behaviour
--fixmode is a separate decision, not this issue.doctormust not be the command that breaks it. Any cloud connectivity probe toupload.xping.iogoes behind an explicit--check-cloudflag.--format jsonalongside the rendered form, same envelope discipline asreport— versioned, stdout only, warnings to stderr.--directory,--ascii,--no-color,NO_COLORexactly asreportdoes, resolved throughOutputCapabilities.Resolve.report:0no failures (warnings allowed) ·1at least onefail·2doctor itself could not run.reportpoints at it.ReportUnavailable's generic "Run your tests once with the Xping SDK installed" is replaced by "Runxping doctorto find out why."Known constraint: the CLI's environment is not the test host's
doctorreads theXPING_*variables of its own process. The developer may have rundotnet testfrom a different shell, from an IDE test runner with its own environment, or in CI. Configuration findings must therefore be phrased as "in this shell" and, wherever possible, cross-checked against what the sessions actually recorded —LocalSessionProperties.Modetravels on every session and is authoritative about the mode the run really used. Where the two disagree, that disagreement is itself the most useful thing doctor can print.Out of scope
--fix/ interactive repair.xping where— it stays, and doctor reuses its data.Open questions
doctorrun automatically whenreportexits 2, or only be pointed at? Pointing is safer: a diagnostic that runs itself unbidden is how a tool gets noisy.*.csprojunder the repo root is cheap but noisy in a large solution; restricting to projects that have written sessions misses precisely the ones that never worked, which are the interesting case.