AppSurface Search
Guide

ForgeTrust.AppSurface.PackageIndex maintainer guide

Source of truth

View source Edit this page

Last updated

ForgeTrust.AppSurface.PackageIndex owns the curated package chooser, readiness dashboard, package-gate policy, and the generated ## Release Guidance region in public package READMEs. Start with the generated package chooser when deciding which package a consumer should install; use this guide when you are changing the repository's package-story policy rather than one package's authored technical documentation.

For the in-progress coverage artifact retention feature, use the canonical coverage storage guide. Its managed default, full-retention choice, and --no-clean interaction are repository source behavior under development; package installation proof and storage measurements are not yet established.

Release guidance

Every managed package README declares one release_guidance_variant in packages/package-index.yml. The value is a finite reader-facing policy choice:

Variant Use when Do not use when
default The package follows the ordinary coordinated prerelease story. A package needs an AppHost-only or publication-held statement.
apphost The package is primarily an AppHost, development, or test integration surface. A runtime package merely happens to have an Aspire example.
experimental The package has an explicitly experimental or publication-held contract. A package needs extra product-specific release prose; keep that prose authored outside the region.

The canonical bodies live in the generator-only release-guidance.template. The non-Markdown extension keeps its required unexpanded URL tokens out of the published Docs graph. Each body expands the package chooser and release-hub links to canonical absolute GitHub URLs. This is deliberate: the package root README.md is included in a NuGet package, but repository-relative targets are not package contents. The package artifact validator confirms that the marked region and both URLs survive packing.

Do not add a fourth variant for package-specific instructions. Keep those instructions outside the managed marker pair:

<!-- appsurface-release-guidance: begin -->
## Release Guidance
<!-- generated content -->
<!-- appsurface-release-guidance: end -->

## Package-specific operations
<!-- authored content -->

The renderer changes only the bytes inside the marker pair. It rejects missing, duplicate, reversed, unknown, or unexpanded markers and tokens rather than guessing, and rejects README paths that cross symbolic links or other reparse points before it reads or replaces them. For legacy README sections with one ## Release Guidance heading, the first generate migration inserts the pair before the next H2 or Markdown horizontal rule so trailing footer navigation remains authored content; after that, retain the markers exactly.

Change workflow

  1. Edit the finite variant or a manifest field in packages/package-index.yml.

  2. Keep package-specific adoption, operational, and proof content outside the generated region.

  3. Reconcile the checked-in outputs:

    dotnet run --project tools/ForgeTrust.AppSurface.PackageIndex/ForgeTrust.AppSurface.PackageIndex.csproj -- generate

    generate states its changed and managed README counts. It validates every target before replacement, stages same-directory temporary files, and rolls back ordinary replacement failures. Inspect and commit the resulting README, chooser, and readiness diffs.

  4. Verify without writing:

    dotnet run --project tools/ForgeTrust.AppSurface.PackageIndex/ForgeTrust.AppSurface.PackageIndex.csproj -- verify
    dotnet run --project tools/ForgeTrust.AppSurface.PackageIndex/ForgeTrust.AppSurface.PackageIndex.csproj -- gate

    verify compares all generated documents and managed README regions; gate validates manifest, template, and marker policy without writing files. Package-gate CI runs both commands.

  5. When a change affects package payloads or published documentation, run the existing package artifact proof:

    dotnet run --project tools/ForgeTrust.AppSurface.PackageIndex/ForgeTrust.AppSurface.PackageIndex.csproj -- verify-packages --package-version 0.0.0-ci.local

    It verifies that the packed README.md has exactly one managed marker pair and exactly one canonical chooser and release-hub URL inside that region.

    The Coverage CLI consumer proof also passes its consumer fixture directory as coverage gate --repository-root when comparing synthetic diff paths with Cobertura paths. A fixture nested inside this checkout must use that explicit root: the CLI's automatic Git-root detection otherwise selects the outer checkout and cannot match consumer-relative paths.

Cloud Tasks packed isolation proof (#892)

When CloudTasks is in the verification plan, verify-packages requires the packed isolation proof before writing a successful artifact manifest. It builds and runs the public GCP consumer from exact validated local archives, checks the complete pinned SDK closure, and compares independent packed baseline and candidate neutral consumers in isolated caches. Invalid-token rejection before provider I/O, direct/transitive dependency isolation, public SDK-type leak scans and artifact hash bindings remain required. The API scan includes generic type and method constraints and protected members exposed to external subclasses. Each new verification attempt removes the previous Cloud Tasks success report before restoring or packing artifacts, so a failed rerun cannot leave that earlier success beside new artifacts. Baseline restore and pack commands resolve project paths relative to their extracted source working directory; this preserves transitive project references when the operating system aliases a temporary directory path.

Ordinary verify-packages runs archive the candidate source commit (producer --source-commit, or canonical HEAD) as the neutral baseline. Main pushes and tagged publication use the same snapshot rule, so later committed neutral source or package-pin updates do not compare against a permanent historical tree. Dirty neutral source/build-input changes still fail parity against the selected committed snapshot. This mode checks current artifact isolation; it does not establish historical no-change evidence.

For strict historical acceptance, supply --cloudtasks-baseline-ref origin/main after fetching the target ref and its full history. The verifier requires one canonical git merge-base --all result with the candidate source commit, records the resolved SHA and mode, and preserves historical source/build parity. A missing or ambiguous merge-base, or a historical ref resolving to the candidate itself, fails with no fallback. Use the reviewed earlier commit/ref for post-merge historical replay; omit the option for ordinary verification. The local orchestration script accepts this as its second argument: ./scripts/verify-cloudtasks-activation.sh /tmp/cloudtasks-acceptance origin/main.

Only the two allowlisted central declarations (Google.Cloud.Tasks.V2 and the project-conditioned Grpc.Net.Client) are normalized. Their versions must match exactly one direct root each in the candidate CloudTasks packages.lock.json; the lock hash is recorded. The historical baseline may omit both declarations; a candidate-source baseline contains both exact validated declarations. Missing, partial, duplicate, mismatched or differently conditioned declarations fail. Every other central policy entry and all neutral source/build inputs remain exact.

The approved plan allows binary differences only for Core, Flow, Workers and Durable after unchanged authored sources/projects, Flow.Generators and shared build inputs are proved. Packed framework/dependency contracts, library entry sets, external package IDs/versions/hashes/edges and each restored local archive binding remain exact; both raw archive and library hashes are retained. Any failed check withholds the manifest. Normalization approval alone proves neither a successful run nor publication authority; use the package guide for local commands and proof limits.

Tailwind artifact provenance (#798)

The reusable native evidence workflow selects the frozen inventory through artifact_manifest_file. Its optional string default is package-artifact-manifest.json for manual rehearsal; both release publishers pass package-artifact-manifest.unapproved.json. Only those two basenames are accepted, and all native proof, mutation and aggregate commands use the selected file without discovery or aliasing. The provenance reference explains the stage boundary: native proof validates the original manifest bytes; protected publication still validates the separate PostgreSQL candidate receipt before consuming the byte-identical approved copy.

The approved #798 design and implementation plan define a release proof binding native Tailwind consumer tests to the exact package bundle passed to NuGet. See the canonical API and operator reference for the implemented CLI flags, evidence schemas, and release workflow.

Authority flows through four stages: the validated tag checkout and package plan establish source and allowed identities; pack-and-verify creates one immutable bundle with the package manifest and producer subject; each native job proves it restored and built from that bundle; then aggregation and publication validate the exact host set and candidate again. Protected workflow outputs establish artifact transport identity. Receipt contents cannot select their own authority. The aggregate has its own immutable ID and JSON digest, separate from the producer bundle's ID and subject digest.

The native consumer starts with the committed consumer lock: its third-party graph is fixed, while the exact Core and Tailwind version and archive hashes come from the producer subject. Both restores run in locked mode. See the operator reference for the update procedure and failure behavior.

The required hosts are exactly linux-x64, linux-arm64, osx-x64, osx-arm64, and win-x64. Native v2 receipts (appsurface-tailwind-native-host-proof-v2) are eligible; v1 is historical diagnostic data only. Each success binds producer ID/run/attempt, subject and manifest digests, source/version, current native invocation, expected and observed host RID/OS/process architecture, and per-first-party producer/restored raw SHA-512 and protected payload evidence. The closure includes Core and Tailwind and comes from the producer's successful net10.0 consumer graph plus validated package inventory. It also records Tailwind release-manifest and selected host CLI digests, generated CSS, absence of companion dependency, and absence of native consumer output. Full modes, flags, report shapes, limits, recipes and recovery are in the reference.

The package artifact workflow, package gate, and prerelease/stable carriers retain the safe cloudtasks-package-consumer-proof.md report in their diagnostic uploads when it exists. A later proof failure therefore preserves the completed isolation evidence. The report is written only after the isolation proof succeeds; its absence from a diagnostic artifact does not identify the failed step. Neither its presence nor the diagnostic upload replaces the complete package workflow's success requirement.

Durable preflight artifact proof (#845)

The schema-11 operations guide defines the candidate, published and deployment gates for complete runtime-set preflight. The release carriers use verify-preflight-artifacts.sh and its disposable package consumer for one pair, enrollment of a second pair, identical reconciliation and a modeled two-pair schema-10 upgrade. The consumer installs the exact packed CLI and restores the matching PostgreSQL provider into isolated roots; it does not repack the product. The version-parameterized consumer project template is not a directly restorable checkout project. The shared script calls the consumer materializer to copy that template, its adjacent C# source files and repository build policy into an owned temporary consumer directory, without copying bin/obj outputs. Restore and both run commands receive the exact package version from the verified bundle; there is no fallback version. This keeps automatic dependency-submission discovery from attempting an unconfigured restore while preserving every package-hash, closure, identity, schema, guard, timeout and receipt gate. The carrier's internal --consumer-project-template input names the source template; the shared script cleans up its materialized project with the rest of the proof's owned directory before promoting a receipt.

The ordinary PostgreSqlPreflightConsumer.csproj is a source-backed, non-packable development project for editing, compilation and generic dependency discovery, including GitHub automatic dependency submission. It references the local provider project. The separate package project template is the artifact-only project selected by the release carrier; its provider reference requires the exact manifest-derived AppSurfacePackageVersion, has no default published-version fallback, and must not be replaced by the source-backed project.

The shared materialize-preflight-consumer.sh stages the package project in the verifier's private scratch directory. It preserves authored sources and nested resources, omits generated bin/obj directories and exactly the same-directory development .csproj, and copies the repository's authored Directory.Build.props and Directory.Build.targets. The staged .csproj bytes match the package template and contain no project reference. Occupied destinations, source/destination overlap, missing build policy and unsupported links are rejected before staging. Nested resources sharing the project basename remain intact. The verifier uses --consumer-project-template for release proof; explicit legacy --consumer-project callers retain an already-materialized package-project path with the canonical PostgreSqlPreflightConsumer.csproj basename. Source ProjectReference inputs are rejected, and the provider package reference must use $(AppSurfacePackageVersion). Both modes are described by the script's help and must not be combined. Artifact receipts still require the exact verified version, frozen first-party feed, complete archive equality checks and checked cleanup. A source build or empty-version restore cannot replace candidate or published artifact proof.

The pre-upgrade schema-10 fixture proves SQL role capabilities: durable Work completion, Flow dispatch discovery, and a Schedule dispatch claim, plus Source dispatcher denials. It retires its unique probes through the restricted runtime and verifies their persisted state before upgrading. Actual registered provider Work, Flow and Schedule execution runs after migration and reconciliation. This modeled baseline does not claim historical binary compatibility. Guard-loss negatives use a separate disposable database with the same package bytes, roles and store identity, retaining canceled lane facts until the fixture container is disposed. The consumer also tests an observer failure while a real provider Work operation waits on a table lock in another disposable database. The controller must cancel and await its lane child and guard monitor before releasing that lock, verify the owned sessions and fence are gone, and withhold the receipt. Failure or timeout during this cleanup blocks the candidate proof. The same checked cleanup covers observation and backend-termination errors in the guard-loss probes; an elapsed wait alone never counts as a drained child. An independent fixture repeats the observer-failure check with a throwing cancellation callback. Nested cleanup must still drain both owned tasks and release their sessions before propagating that callback failure. An actual verified activation-drain failure follows the same cleanup path: it drains the guard monitor, releases the guard, and finishes and disposes the queued writer, while withholding the receipt.

Candidate proof runs for each bundle in both release channels before the artifact manifest can authorize publication. A failed command or missing, incomplete, mismatched or stale receipt leaves that gate closed. The published smoke carrier then compares restored package contents and extracted role-recipe bytes with the candidate and runs the same proof. An earlier candidate result cannot stand in for the published proof. Source tests and tool help remain separate checks.

The carrier binds sourceCommit, runId, and the immutable producer artifactId to the receipt and checks every first-party archive's nuspec package id, package version, canonical repository URL, and repository commit before the consumer runs. It rechecks the candidate bundle after the run. The public smoke restore explicitly requests every publish and support_publish manifest entry, including support packages unreachable from public dependency roots. Each restored archive must have the same ZIP entry names and uncompressed contents as its candidate, except the optional root .signature.p7s envelope added or updated by NuGet repository signing. Duplicate entry names, changed package metadata, added/missing payloads, or a changed extracted PostgreSQL role recipe close the gate. ZIP compression, entry order and timestamps are not payload identity.

The shared consumer runs from copies of the approved candidate archives, whose original manifest SHA-512 values are checked before and after execution. Passing signed public archives to that candidate manifest would invalidate its hash binding. The version-2 .carrier.json uses ProofKind: issue845-public-feed-package-content-identity and records CandidatePackageSha256 and PublicPackageSha256 maps for every manifest package, plus the candidate receipt and shared proof receipt hashes. These raw archive hashes may differ after signing. The carrier proves payload identity; NuGet restore owns signature validation, and the carrier receipt makes no independent signature-authenticity claim. Retain both receipts and the original manifest; a whole-archive public hash must never replace a candidate manifest hash.

The smoke workflow obtains library archives from its isolated NUGET_PACKAGES cache. Tool archives come from the fresh dotnet tool install --tool-path store at <tool-path>/.store/<lowercase-id>/<version>/<lowercase-id>/<version>/<lowercase-id>.<version>.nupkg; the SDK does not put those archives in NUGET_PACKAGES. After each tool's help and exact-version checks pass, the workflow copies that installed public archive into the proof cache without rewriting any bytes. It replaces a stale cache copy and rejects a missing or linked store archive or linked staging destination. This copy only locates the public bytes: the carrier still compares every payload entry before invoking the shared proof. A candidate archive or an existing cache entry cannot substitute for a missing installed tool archive.

The promoted Durable template replay also compares every manifest archive, so installed tool archives are staged before it begins. Both that replay and the published runtime-preflight proof require successful library and tool smoke; the report retains those results when a later proof fails.

Each canonical CLI result row carries a Scenario identity. The one-pair scenario proves forwarder Work, Flow, and Schedule before and after; the installed work_only pair scenarios also prove Work succeeds while Flow, Schedule, and All are denied. Each before/after lane record binds its scenario, store, epoch, guard backend, and runtime-role results. The queued-writer receipt uses the additive outcome values completed-before-writer or bounded-failure-then-rerun, with authoritativeScenario bound to the fourth complete CLI evidence set (queued-writer-prewriter-complete or queued-writer-postwriter-rerun). The first branch records that the complete window finished before the writer acquired its lock; the recovery branch records a bounded failure, writer release and completion, then the complete post-writer window. This discriminator is an additive receipt-contract correction for the writer outcome union; the producer and shared contract must emit the same fields before release receipts can pass.

The internal artifact-proof request supplies the repository root, frozen artifact directory, caller-owned manifest path, exact source commit, carrier run identifier and artifact identifier. A successful candidate proof moves the manifest to the approved path; on failure, the carrier preserves it at the caller-supplied path, rolling back a partial promotion when that path remains available. Candidate and published operations propagate cancellation to bounded child commands, validate complete proof receipts and retain package SHA-256 values alongside the artifact manifest's SHA-512 binding. Proof receipts contain synthetic role/store/epoch identity and stage timings; connection credentials stay in child-process environment variables. Consult the generated --help for the explicit carrier arguments, and retain the candidate receipt with the original bundle for the public smoke invocation.

The internal Program.RunAsync command boundary accepts an optional preflightCommandRunner for verification of candidate and published command dispatch. Omitting it uses CliWrapCommandRunner to launch the real bounded consumer process. The override changes only that external-command boundary: argument parsing, archive and receipt validation, checked scratch cleanup, and manifest promotion still run through the production carrier. Promotion validates retained evidence without launching a consumer. This is an internal test seam, not a CLI option; injected fixture results do not replace the exact-package proof.

The fixture owner guard spans distinct runtime invocations, lane checks and a cancellable fixture activation callback. A released receipt records a completed disposable proof window; it cannot authorize a later deployment. An actual application activation requires independently reviewed inputs, named owners and its own continuous session-affine coordinator, as described in the canonical operations guide. Keep the existing release job deadlines: this proof does not authorize increasing them or skipping a scenario after a timeout.

For production smoke-install, pass --artifacts-input for the downloaded frozen bundle, --artifact-manifest for its promoted manifest, --preflight-source-commit, --preflight-run-id, --preflight-artifact-id, and --preflight-candidate-receipt. The bundle directory must contain the receipt-bound candidate archives; the manifest path alone does not select that directory. The command validates the retained candidate receipt before public-feed restore evidence can authorize the shared proof. Its published receipt and .carrier.json record are written under the smoke work directory and should be retained with the smoke report.

Python parser candidate gate

The inspect-python-parser-candidate command is a bounded, static dependency-selection proof. It accepts one local .nupkg, enumerates its native runtime assets, NuGet metadata, and license/notice paths, then writes JSON containing the archive hash, compressed size, RID inventory, and rejection reasons. It never adds, restores, builds, loads, or executes candidate package content. A disposable local feed or child process would not sandbox untrusted NuGet build assets, managed assemblies, analyzers, or native libraries.

dotnet run --project tools/ForgeTrust.AppSurface.PackageIndex/ForgeTrust.AppSurface.PackageIndex.csproj -- \
  inspect-python-parser-candidate \
  --python-parser-package /tmp/candidate.nupkg \
  --python-parser-proof-report artifacts/python-parser-proof.json

An exit code of 0 means the inspection completed and the report was written; it does not mean the candidate was accepted. Reports must be written below the repository's artifacts/ directory. Read rejectionReasons and isEligibleForFurtherReview from the JSON before taking any dependency action. Archive size is recorded as decision evidence rather than a hard product budget; the command's separate 64 MiB archive-read limit protects the inspection process itself. Report destinations are immutable: choose a new filename for every run, because existing files and symbolic-link paths are rejected rather than overwritten.

The source-controlled TreeSitter.DotNet 1.3.0 candidate record documents the accepted bounded Python-docstring spike. Its 50.93 MiB all-grammar, multi-RID archive is an explicit distribution trade-off, not a reason to exclude it from product code. Package upgrades must repeat the archive, native-RID, and redistribution-notice review.

Recovery and release boundary

If generate reports a marker, variant, path, or token error, fix the named manifest row or README and rerun the command. If a write is interrupted, rerun verify to identify drift, then rerun generate, inspect the diff, and rerun verify. Do not edit the generated region by hand as a substitute for updating its template or manifest field.

Package README reconciliation is intentionally separate from the release authoring checklist. eng/release prepare, including its dry run, must not regenerate package README files; release preparation validates its own exact artifact set while PackageIndex remains the owner of checked-in package-policy documentation.

Release-preparation witness

When a release-preparation pull request also changes generated package documentation, the Release verifier invokes this read-only command once:

dotnet run --project tools/ForgeTrust.AppSurface.PackageIndex/ForgeTrust.AppSurface.PackageIndex.csproj -- release-prep-witness --base-ref <base-tip-commit> --witness /tmp/appsurface-release-prep-witness.json

It does not write chooser, readiness, or README files. Instead it records the base tip, exactly one merge base, HEAD, changed semantic sources, and deterministic SHA-256 hashes for the chooser, readiness dashboard, and each managed README body. Only a changed packages/package-index.yml or release-guidance.template can authorize those surfaces; packages/README.md.yml is hand-authored metadata and is never an input. When an authorized input changes a surface relative to the merge base, the release pull request must commit that surface and match the witness digest; a partial PackageIndex regeneration is rejected. The Release verifier rejects unknown, duplicate, unordered, unsafe, or uppercase-hash JSON values, and it requires a README's bytes outside the managed marker body to be identical to the merge-base version. Treat --witness as an advanced CI/test seam; use ./eng/release verify-prep-diff --base-ref main as the normal front door.

Adding a variant

Adding a variant is a policy change, not a per-package escape hatch. Document why the existing three variants cannot express the reader-facing posture, add one exact template pair in release-guidance.template, extend the renderer's finite allowlist and tests, update this table with a non-example, and add the package artifact proof. Otherwise, use an existing variant and preserve the package-specific explanation outside the marker pair.

Durable worker template proof (#806)

The Durable worker start page explains the generated host and three-command first Work. The template is a native dotnet new package; candidate packing stages an owned content copy and stamps only coordinated package version fields in the host/test project graph and their shared Directory.Packages.props. It never rewrites authored C#, accepts an install-time version parameter, or references source projects from a generated application.

verify-packages runs the authored-source and exact-template consumer proof when the publish plan includes the template. An installed-template proof can also be repeated against a frozen producer bundle:

dotnet run --project tools/ForgeTrust.AppSurface.PackageIndex -- verify-durable-template --repo-root . --artifacts-input <producer-download> --artifact-manifest <producer-download>/package-artifact-manifest.unapproved.json --package-version <exact-version> --source-commit <full-sha> --report <safe-receipt.json> --native-pg-bin <resolved-native-bin>

The command reads the exact producer manifest and rechecks every archive hash. It verifies native path installation/discovery/create/uninstall and feed/package-ID installation in independent CLI-home and package-cache roots. Acquisition maps ForgeTrust.* exclusively to the candidate feed; the final authored/path/feed consumer restores have only that feed and cleared fallback folders. Each consumer restores, builds with warnings as errors, verifies formatting and runs generated tests excluding the PostgreSql and NativePostgreSql categories. Proof child environments disable MSBuild node reuse, the CLI MSBuild server and shared C# compilation so their process lifetime stays within the owned workspace; these settings apply only to verifier children, not the generated application configuration. The Linux job separately runs the pinned-image FirstDurableWork certificate. --native-pg-bin requests a private native cluster and ordinary-startup proof on all three OS; omitted native setup earns no native-smoke assertion.

The archive contract requires exactly one NuGet core-properties record. It accepts the exact, case-sensitive path package/services/metadata/core-properties/nuget.psmdcp used by current NuGet producers, or the same directory with a legacy hexadecimal/hyphen basename and lowercase .psmdcp suffix. Near-miss names, extra records and duplicate or case-colliding paths fail validation. This compatibility allowance preserves the archive bounds and generated-content identity; it does not admit arbitrary metadata filenames.

On macOS, the evidence workflow uses the native PostgreSQL acquisition helper. It queries brew --prefix --installed postgresql@16, installs the formula once when absent, then resolves the physical bin directory and verifies all four required tools. A plain brew --prefix can succeed without an installed keg; it is unsuitable as the acquisition check. Homebrew's opt symlink must resolve to the physical keg so the postmaster's executable path matches the verifier's ownership identity. The helper writes only that absolute bin directory to standard output, sends installation diagnostics to standard error, and fails without publishing a partial tool path. It starts no PostgreSQL service or cluster; acquisition remains within the workflow's native job timeout.

The native smoke contract uses /tmp for its private cluster root on Linux and /private/tmp on macOS. Windows and other platforms use the system temporary directory. Linux and macOS use these short roots instead of TMPDIR because the socket-path guard enforces PostgreSQL's unchanged 103-byte Unix-domain socket limit. The root override applies only to the native cluster; the generated smoke still fails closed if its selected root cannot satisfy ownership or path checks.

Native setup shares one 90-second deadline across initialization, startup, ownership checks, authentication and server-version verification, followed by a separate 20-second cleanup budget. Tool acquisition has its own five-minute deadline. The internal NativePostgreSqlBudgets test seam accepts positive lower setup and cleanup limits for bounded regression tests; it cannot raise the production ceilings. Every setup request receives at most the remaining portion of the selected setup budget. Setup authentication also retains its five-second per-probe cap, taking the smaller limit; standalone authentication after setup keeps the five-second cap. A lower test budget applies to initdb and the final psql version probe as well as startup and authentication; advancing to another phase does not reset it. The native-smoke child setup allowance and its runner timeout (that allowance plus the existing 35-second execution overhead) use one snapshot of the remaining setup budget, so a clock tick between command fields cannot give them different allocations. These limits are verifier contracts, not generated application settings.

The native bootstrap child uses PostgreSQL's OSTYPE=msys Windows prompt fallback to read the redirected password input instead of CONIN$/CONOUT$. This value is set only in that Windows child's launch environment; Unix and parent environments are unchanged. The password remains on stdin, SCRAM authentication and restricted-token behavior remain enabled, and timeout/termination/output-drain checks remain required. This removes a console-input dependency; a passing test of launch settings alone does not qualify a hosted native cluster. See the native proof contract for required authentication and cleanup evidence.

Disposal lock acquisition and cluster teardown consume the same cleanup allowance. A concurrent caller times out while the active disposer retains ownership; waiting for that lock never resets the deadline. A cancellation during ownership-marker creation removes this invocation's incomplete marker before ownership-checked partial-root cleanup and preserves the original cancellation token.

Native server identity accepts a numeric version with an optional complete parenthesized distribution label, such as 16.5 (Ubuntu 16.5-1). The receipt retains the numeric version. Its major and minor release numbers must match both the validated tool version and PostgreSQL's integer server_version_num, with the PostgreSQL 16 floor preserved. Arbitrary suffix text, malformed labels and contradictory numeric identities fail before the generated smoke runs; the owned cluster is still stopped and cleaned up.

The receipt is bounded schema-v1 JSON (64KiB, depth16), emitted after owned cleanup. It contains a full source revision, every producer package ID/version/SHA-512, deterministic generated-content SHA-256, SDK/RID/image, fixed phase identifiers with elapsed seconds/exit/truncation status, assertion booleans, safe failure code/phase, cleanup status and native tool/server identity. It retains no command arguments, process output, payloads, bearer credentials or connection strings. A failed, incomplete, truncated or uncertain-cleanup proof cannot authorize publication. These records rely on the existing trusted workflow and immutable artifact downloads; they are not portable signatures. See artifact provenance for the separate existing trust boundary.

For failures after replacement commands return, FailurePhase identifies the local check that failed: replacement-stage, replacement-apply, replacement-graph, feed-graph, or sql-identity. Command failures retain their command phase, and native callback failures retain the more specific native subphase when available.

Test phases additionally carry an optional TestRun observation from the TRX logger: Status is observed, missing, or invalid; Total, Executed, Passed, and Failed are bounded counters; FailedMethodSha256 contains up to 16 distinct hashes sorted ordinally. Each hash is SHA-256 of UTF-8 TestMethod.className, a newline, and TestMethod.name, using the exact XML attribute values (including any assembly suffix). Match those hashes against the known generated test methods when a failed phase needs triage. Hashes group theory cases by their method identity rather than retaining display names or arguments. Raw TRX files, failure messages, stack traces and child streams stay in the temporary proof workspace; the results directory permits only its owner on Unix. Owned cleanup deletes the files, but uncertain cleanup can retain the workspace and must be investigated before removal. The reader accepts at most one MiB of regular TRX XML, prohibits DTDs and external entities, and caps counters at 10,000 tests and method attributes at 1,024 characters each. Missing, oversized or malformed diagnostics report zero counters and no method hashes. This observation is diagnostic only: it never replaces the command exit code, truncation checks, checkpoint assertions or publication validation. An exit-one test phase with a missing report remains undiagnosed; observed counters can distinguish reported assertion failures from a process failure with no failed test case.

Failed generated FirstDurableWork cases can additionally carry TestRun.FirstWorkFailures: at most 16 distinct records containing Phase, Code, and Cleanup. The internal DurableTemplateFirstWorkDiagnostic parser accepts the fixture's fixed failure marker only from failed TRX results bound to the exact first-work method and class. Known phase and exception-category values are retained; an unknown code becomes Other, an unknown cleanup observation becomes unknown, and unknown phases or ambiguous duplicate markers are withheld. It inspects at most one MiB of output and rejects marker lines over 4,096 characters. Arbitrary marker text, exception messages, stack locations, paths, arguments and retry prose are never copied into receipts. This additive field is absent when no valid marker exists, including successful tests. It helps select a reproduction after a failure; it cannot establish a cause by itself or change any command exit, checkpoint, timing, cleanup or publication gate. See the first-work recipe for the actual execution contract.

For postgresql-fixture-setup failures, an optional SetupOperation projects reviewed async fixture method names to one of three closed labels: docker-prerequisite, pinned-image-acquisition, or owned-fixture-setup. The last label includes both container startup and provisioning. The parser examines at most four method frames of at most 256 characters each and accepts only an exact reviewed state-machine name with an ASCII numeric ordinal and .MoveNext suffix. Unrecognized frames are ignored; no reviewed frame, conflicting specific operations, or exceeded frame bounds omit the label. A specific preflight operation takes precedence over its enclosing setup method. No raw frame text is retained. An absent label means the operation is undiagnosed. A label identifies a setup boundary, not its underlying cause or infrastructure ownership; Cleanup: none still reports only the outer test's cleanup observation and does not rule out cleanup inside fixture setup.

The package artifact workflow and package gate workflow retain durable-template-consumer-proof.json in their package-validation diagnostic artifacts when the installed consumer proof fails. Both workflows always attempt diagnostic upload. The artifact workflow reads the receipt from package-artifacts; the policy-selected package gate reads it from package-evidence-*/package-checks/artifacts beneath the runner's temporary directory. Inspect the receipt's safe failure phase and any TestRun.FirstWorkFailures marker before choosing a reproduction; the failed proof still withholds the package manifest. The upload allowlist includes only this bounded receipt for the Durable proof, so its private child and provider streams and the isolated package cache remain outside the diagnostic artifact. If execution fails before a receipt is written, the diagnostic upload may contain no Durable receipt. The C# transport contract tests, run by the build before coverage, verify these paths, exact artifact IDs, publisher boundaries and native PostgreSQL acquisition.

The optional NativeAcquisition field records validated same-directory executable paths, version and SHA-256 hashes as soon as tool acquisition succeeds, even when initialization subsequently fails. It is separate from NativeTools, which is retained only after a successful native smoke. Acquisition has no verified server version and does not certify startup, authentication, cleanup or publication. Failure before validation leaves it absent. Use it to match a Windows initialization failure to its actual binary rather than inferring a version from the runner image.

Native pg_ctl start opts into the internal command runner's ExternalCapturePolicy.CompleteOnProcessExit policy. PostgreSQL's Windows launcher retains a command-shell child for the server lifetime, which can keep inherited output handles open after pg_ctl exits (upstream explanation). Waiting for those streams to reach EOF would consume the setup deadline even after successful startup. The policy ends only output observation on direct-child exit, retains the actual exit code, and preserves command cancellation and the shared setup deadline. Captures whose EOF was not observed have their existing truncation flag set and carry an output incomplete after process exit marker; retained bytes stay under the configured cap. The policy defaults to false and must not be used for complete structured-output proofs. All other commands, including authentication, version probes and cleanup, still drain to EOF. A successful launcher exit alone does not prove readiness: owned-postmaster identity, the listening port, SCRAM authentication and matching server version remain required before the native smoke proceeds. Raw child diagnostics remain private.

For a failed native callback, FailurePhase projects a closed observation stage: native-acquisition, native-initdb, native-startup, native-ownership, native-authentication, native-smoke-execution, or native-cleanup. Initialization results further distinguish native-initdb-exit (verified nonzero exit), native-initdb-timeout (deadline exceeded with verified termination), native-initdb-adapter (no structured bootstrap result), and native-initdb-termination (termination or stream settlement unverified). The last outcome normally becomes native-cleanup when uncertain termination prevents safe removal of the owned root. Legacy or otherwise unclassified initialization failures retain native-initdb. These labels identify observed outcomes, not underlying causes; for example, an exit label does not diagnose a credential or console problem. Startup observations distinguish native-port-check (the selected port's pre-start check), native-start-command (the pg_ctl adapter failed without a structured result), native-start-command-exit (a nonzero pg_ctl exit other than the runner's -1 sentinel), native-start-command-incomplete (the -1 sentinel), and native-server-version (the probe failed or its identity did not match the acquired tools). The incomplete label does not prove a timeout: the external command runner also uses -1 for launch and adapter failures. None of these labels includes child output, paths, credentials or exception text, and none changes the setup deadline, cleanup checks or rejection of failed proof. Older receipts with native-startup cannot be retrospectively classified; rerun on the failing host with the same candidate inputs. Unannotated or unsupported stages retain native-smoke. The internal NativePostgreSqlFailure helper preserves the first typed annotation on an exception and copies only supported stages into sanitized setup wrappers through CopyAnnotation(source, target). Existing target annotations take precedence; missing or undefined source stages and unrelated metadata are ignored. The helper reads only its private stage key; it never projects exception text or arbitrary metadata. Use the stage to choose the next target-host diagnostic, rather than infer a root cause from it. NativeTools is absent after any failed native callback, including smoke or cleanup failure, so its absence does not establish that setup failed. Every failed receipt remains rejected by the release evidence gate.

The blocking template evidence workflow consumes the exact producer artifact ID and source revision, runs Linux x64/macOS arm64/Windows x64, and returns five immutable receipt artifact IDs for the OS and timing proofs. Release jobs download those IDs, then run verify-durable-template-evidence --artifacts-input <producer> --artifact-manifest <manifest> --durable-template-evidence <receipt-download> --preflight-source-commit <full-sha> before requesting a NuGet publishing token. The publisher repeats the same validation before reading the credential. No receipt may choose its own expected source or substitute packages. OS receipts retain the observed hosted ImageOS/ImageVersion as RunnerImage (two ASCII labels of at most 63 characters each); missing or unsafe observations produce an empty local diagnostic identity and are rejected for publication. The workflow requires both observations before executing its proof. These labels share the trusted artifact provenance boundary and do not establish portable authenticity. The existing preflight artifact proof retains ownership of schema-10/current/prior-binary evidence.

DurableTemplateConsumerProof.RunAsync takes immutable inputs and validated artifact rows, an optional owned native callback and a safe native identity accessor. The callback receives only a generated root and private child environment; it must stop its own acquired resources before returning. Cancellation stops work but still triggers independently bounded uninstall and checked owned-root cleanup. Only unique owned temporary roots are deleted; link/reparse components are rejected. DurableTemplateReleaseEvidence parses bounded receipts, rejects duplicate/unknown fields, and compares successful assertions and every candidate hash before credential access. Internal APIs are exposed only to the repository test assembly; deterministic command, tool-directory and process seams exercise failure/lifetime branches without reflection.

PackageArtifactWorkflow accepts an internal optional templateProof delegate for testing its release orchestration. The default invokes the installed consumer proof with validated artifact rows, candidate version/source and the acquisition feed. Packing always cleans its private staged content before calling the proof. A failed receipt or thrown proof prevents the artifact manifest from being written; tests can exercise that boundary without installing packages or launching PostgreSQL. This seam does not replace the separate exact-artifact release checks.

verify-durable-template-timing measures five serial install/create/test workloads on a Linux runner. Supply the same exact producer/source inputs above plus --clock primed or --clock cold; cold requires --docker-hosts with five private workflow-owned bridge endpoints. Each sample owns separate CLI, package, HTTP-cache, project and database identities. HttpCacheEmptyAtStart is measured from the private HTTP-cache directory after Docker preflight and immediately before the sample clock starts; both modes require it to be empty, and the evaluator rejects a sample that reports reuse or population. Primed preparation hashes the full restored NuGet cache tree by normalized relative paths and exact file bytes, including extracted package files, archives, metadata, and empty directories; links/reparse points, more than 16,384 tree entries, any file over 256 MiB, or more than 1 GiB of files fail closed. Each sample receives a copy of that same root-independent closure, and its measured cache hash must match the prepared closure. The pinned image is pulled outside the clock. The install/create/restore/build group has a 55-second cap; the complete workload includes cleanup and must remain below 180 seconds, with a five-run median below 120 seconds. Cold diagnostics start with empty package caches and five independently verified fresh daemons, retain five samples and nearest-rank p95 (the maximum), and have a 900-second watchdog per sample without a cold performance promise. The workflow never purges a shared cache or daemon.

The generated fixture supplies a private monotonic boundary file when the repository runner sets APPSURFACE_TEMPLATE_TIMING_READY_FILE; normal developer commands need no such setting. The runner uses it to stop the group deadline after restore/build, while preserving the full workload deadline. Receipt command records contain fixed phase enums and hashes of argument vectors, never raw arguments. Failed attempts remain in the receipt and invalidate the series; a corrected run uses a new series ID. The safe receipt includes the clock frequency so the publisher can recompute every duration, assertion and summary, rather than trusting a supplied success flag.

Timing observations accept xUnit's spaces or tabs before a complete cleanup marker and normalize the generated database SHA-256 from either hexadecimal letter case to the receipt's lowercase form. These transport allowances retain the requirement for exactly one cleanup observation below the original 20-second ceiling and exactly 64 hexadecimal database-identity characters. Missing, duplicated, malformed, exhausted or truncated observations still invalidate the sample; parsing never renews its cleanup allowance.

The evidence workflow supplies five immutable artifact IDs: three OS correctness receipts plus timing-primed.json and timing-cold.json. Both timing receipts must match the full frozen candidate archive set and source revision. DurableTemplateReleaseEvidence.ValidateTiming reevaluates all samples against the unchanged timing policy before credential access. Candidate measurements are labeled CandidateLocal; public replay installs the promoted template using the native public-feed command, compares its complete unsigned payload with the frozen producer, and executes an independently labeled consumer proof. A candidate result makes no claim about public download speed.