AppSurface Search
Guide

EvidenceHost: risk-mediated CI

Plan and verify CI evidence by changed-risk profile so meaningful changes are tested visibly while explicitly low-risk changes avoid coverage theater.

Source of truth

View source Edit this page

Last updated

Status

public preview CI evidence guide

EvidenceHost resolves only checked-in policy and explicit changed paths; it does not infer test completeness or silently promote observations into gates.

Safe To Consume

Scope: Policy planning, producer lifecycle, and claims only. Consumer tests, resources, thresholds, and release decisions remain consumer-owned.

Freshness: Keep aligned with Evidence package READMEs and the appsurface evidence command surface.

Record

v1 does not provide independent artifact attestation, cross-job aggregation, a semantic low-value-line classifier, or a hosted dashboard.

  • Evidence/ForgeTrust.AppSurface.Evidence.Contracts/README.md
  • Evidence/ForgeTrust.AppSurface.Evidence.Planner/README.md
  • Evidence/ForgeTrust.AppSurface.Evidence.Cli/README.md
  • Evidence/ForgeTrust.AppSurface.Evidence.Aspire/README.md

EvidenceHost is AppSurface's contract-first approach to CI evidence. Its purpose is simple: make a meaningful change visibly prove the risk it introduces, while allowing an explicitly low-risk change to pass without someone inventing tests for incidental lines.

It is not a replacement for a test framework, coverage collector, GitHub Actions, Aspire AppHost, Docker, or a hosted coverage dashboard. It starts no infrastructure by default and has no outbound telemetry.

The first five minutes

Install the AppSurface CLI, then create the deliberately small policy starter:

appsurface evidence init --sample
appsurface evidence doctor --path docs/README.md
appsurface evidence explain --path src/Orders/SubmitOrder.cs

The generated policy maps docs/** to an explicit no-evidence profile and sends every unmatched path to a non-empty conservative coverage profile. explain tells a developer, before tests start, which profile won, which rule selected it, which producers must run, and which obligations they must close.

Evidence plan: targeted-coverage (targeted)
Why: conservative:targeted-coverage
Changed paths: src/Orders/SubmitOrder.cs
Obligations: changed-behavior-covered
Required producers: coverage
Required resources: none

That is the desired “quiet when right, obvious when wrong” interaction: a docs-only PR does not chase coverage; a behavior-bearing change states its required evidence up front.

What a claim means

EvidenceHost has four honest outcomes:

Claim May gate? Meaning
TargetedComplete Pull request Every selected producer passed and closed every selected obligation.
ReleaseComplete Release only The release profile completed and an explicit CI envelope verifier accepted the run. v1 is still not independently attested.
NoEvidenceRequired Pull request A checked-in, explicit empty profile matched the changed paths. It never means “tests were skipped.”
ObservationOnly No A useful diagnostic run that cannot satisfy a gate.

Anything else has ClaimKind.None: a filtered test suite, missing browser runtime, unavailable Docker dependency, timed-out producer, or unsatisfied assertion is incomplete evidence—not a partial pass. This is intentional. A repository-wide gate on an incomplete test profile is a configuration error, and EvidenceHost makes that condition visible instead of encouraging a misleading green badge.

Choose the smallest path

Situation Use Why
Existing coverage command needs clearer scope and artifacts. appsurface evidence Policy planning, diagnostics, canonical plan/manifest, and GitHub summary.
An Aspire resource needs its native healthy condition only. Aspire resource notifications Use Aspire's health wait when resource health is the complete precondition.
A test needs one profile-based AppHost started and an HTTP status condition checked. Typed AppSurface Aspire fixture and the neutral HTTP readiness package One startup budget covers typed profile creation, build, start, native health, endpoint allocation, and the HTTP check. Ordinary tests do not need an EvidenceHost plan or producer.
Consumer has PostgreSQL, a browser, or a real E2E journey to prove. ForgeTrust.AppSurface.Evidence.Aspire A separate explicit EvidenceHost waits for consumer-owned readiness, runs typed producers, and cleans them up. Its optional HTTP adapter composes native health with one declared HTTP condition.
You are writing a producer, external gate, or policy editor. ForgeTrust.AppSurface.Evidence.Contracts and Planner Stable contracts and deterministic diff-to-plan resolution.
You only need numeric Cobertura thresholds. appsurface coverage gate Keep the current private, local coverage gate; EvidenceHost does not hide its configured threshold.

Aspire HTTP readiness

The neutral readiness package reference documents HttpReadinessProbe.WaitAsync, its defaults, safe diagnostics, and transport limits. Use its standalone probe when the application and HttpClient already exist. Use Aspire's native HTTP health checks when they fully express the required condition. Add the typed startup fixture when the test also needs the profile-based AppHost lifecycle under the same deadline.

Readiness checks only the declared HTTP status condition after Aspire reports the selected resource healthy. The default predicate is exactly HTTP 200; it does not read a body or prove browser behavior, authentication, business state, or evidence obligations. Keep those assertions in the owning test or producer. The deadline is cooperative: synchronous factories and callbacks, transports that ignore cancellation, and awaited cleanup can outlast it.

Each adapter created by an Evidence.Aspire application lease retains its own application registration. Disposing an HTTP readiness adapter closes admission and drains that adapter's HTTP waits before releasing its registration. A health-only adapter retains a registration but does not cancel or drain its native health wait; finish or cancel and await that wait before disposing the adapter. Only the final adapter release stops the application. Dispose every adapter, and finish all readiness work before directly disposing the application lease.

Keep test and application hosts separate

An EvidenceHost is not a specialized production AppHost. Keep it in consumer test/CI composition and register every resource and producer explicitly:

await using var host = EvidenceHostBootstrap.Create(plan, registration =>
{
    registration.AddResource(postgresReadiness);
    registration.AddProducer(orderSubmissionBrowserE2e);
});

var manifest = await host.RunAsync();

There is no assembly scanning, ambient resource discovery, hidden Docker provisioner, or automatic E2E selection. The policy is the control plane; the consumer's code owns what “ready” and “asserted” mean.

What v1 deliberately does not decide

The first release does not classify arbitrary C# constructors, property accessors, generated code, or “trivial” lines as low value. A heuristic with false negatives would let a behavior-bearing change escape. Existing coverage exclusions remain the appropriate mechanism for known generated or excluded source. A future classifier must be versioned, observable, and conservative before it can alter a gate.

Similarly, v1 validates a registered release envelope but labels it ValidatedNotAttested. It does not claim artifact attestation, cross-job aggregation, Docker sandboxing, trend analytics, or an AppSurface-hosted dashboard.

For a complete native example that validates AppSurface's own packed CLI and installed consumer, use the native EvidenceHost acceptance recipe. It demonstrates bounded commands, owned storage, independent assertions and durable manifest output.

AppSurface's own native CI policy now selects those journeys from the real PR diff. The local development command uses the same policy for committed, staged, unstaged and untracked changes, packing only when required. The shared CI recipe documents profile combination, conservative fallback, retained artifacts and manifest gates. The same policy now selects full solution coverage and package checks as well as native acceptance. Local all runs every selected producer; CI executes verified native, coverage and package projections without repeating unrelated producers on each platform.

For the in-progress coverage artifact retention behavior, see the coverage storage guide. Evidence coverage producers default to managed retention, accept an additive coverageArtifactRetention value of managed or full, and keep the legacy declaration constructor shape. Retention changes raw duplicate preservation only; it does not change collection or gates. The CLI's --no-clean preservation boundary wins in either retention mode. The guide describes the source change under development; it does not assert installed-package proof or measured savings.