# Example: ASP.NET Core API client package packaging

`package.md` records how a Core API client package is built, what it contains,
and where it is published. For an SDK-style project the `.csproj` is the
packaging manifest, so the page reads identity values from the project and says
what supplies any value the project does not set. Doc gen produces one per
client at `docs/clients/<client-slug>/package.md`.

## What reviewers should check

- The project SDK is `Microsoft.NET.Sdk`. A `Microsoft.NET.Sdk.Web` project is
  an API host, never a client package.
- `## Identity` names the MSBuild property that supplied each value. A value the
  project does not set, typically `Version`, says what supplies it instead (the
  pipeline build number format) and is never printed as if it were declared.
- The page uses .NET Core vocabulary only: `<PackageReference>`,
  `<ProjectReference>`, `Program.cs`. No `packages.config`, no
  `AssemblyInfo.cs`, and no nuspec is treated as the manifest. A nuspec beside
  an SDK project is a finding unless the project sets `<NuspecFile>`.
- `## Dependencies` leads with `SAIF.NetCore.ServiceExtensions` and
  `SAIF.Http.ResponseValidator`, then names any package-specific dependency.
  A dependency beyond the SAIF HTTP and service packages is carried into every
  consumer, so it is a real finding, not filler.
- `## Bundled project references` records the `PrivateAssets` metadata exactly.
  A reference marked `PrivateAssets="All"` is bundled into `lib/<tfm>` and not
  emitted as a package dependency; the unmarked case is raised in Tech debt.
- `## Shipped content` lists only files the project actually produces. Rows for
  symbols or a contracts assembly appear only when the project's packing
  properties and targets declare them.
- The build `AppType` of `CoreApi` describes the repository, not the package.
  `## Feed` and the publish trigger are evidenced by a pipeline or recorded as
  "Not determined from source". An inferred feed name is a defect.

## Example

The document below is a complete, correct `client-package.md` for a fictional
ASP.NET Core API client package.

[View rendered example](rendered/clients/saif-premium-api-client/package.md) — the same document as a real page, so you can compare rendering as well as source.

````markdown
---
title: Package
summary: SDK project manifest, version, dependencies, bundled project references, shipped content, feed, and build pipeline.
status: draft
owner: Premium Systems Team
last_reviewed: 2026-10-21
---

# Package

`SAIF.Premium.Api.Client` is an SDK-style `net8.0` class library that
`dotnet pack` turns into a NuGet package straight from its `.csproj`; there is no
nuspec. The project does not set a version: the `PremiumApi_Client` pipeline
supplies one from its build number, in the form `8.2026.1021.1`, and branch
builds carry a branch-name suffix. The package ships the compiled client, its
symbols, and the `SAIF.Premium.Api.Contracts` assembly bundled from a project
reference. The feed and the publish trigger are not stated in the repository.

## Identity

| Field | Value | Supplied by |
| --- | --- | --- |
| Package id | `SAIF.Premium.Api.Client` | `<PackageId>` in the project |
| Version | `8.2026.1021.1` (example: first build on 21 October 2026) | Pipeline build number (`name:` format `$(MajorVersion).$(Year:yyyy).$(Month)$(DayOfMonth).$(rev:r)`); no `<Version>` or `<PackageVersion>` in the project |
| Target framework(s) | `net8.0` | `<TargetFramework>` |
| Authors | `SAIF Corporation` | `<Authors>` |
| Description | Typed .NET 8 client for the SAIF Premium API (premium, class-code and rate-table endpoints). | `<Description>` |
| Packable | true | `<IsPackable>` |
| Pack on build | Not set | `<GeneratePackageOnBuild>` is absent |
| Project file | `src/SAIF.Premium.Api.Client/SAIF.Premium.Api.Client.csproj` | - |

## Package manifest (SDK project)

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <PackageId>SAIF.Premium.Api.Client</PackageId>
    <Authors>SAIF Corporation</Authors>
    <Description>Typed .NET 8 client for the SAIF Premium API (premium, class-code and rate-table endpoints).</Description>
    <IsPackable>true</IsPackable>
    <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
    <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="SAIF.NetCore.ServiceExtensions" Version="8.2.1" />
    <PackageReference Include="SAIF.Http.ResponseValidator" Version="8.0.3" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\SAIF.Premium.Api.Contracts\SAIF.Premium.Api.Contracts.csproj" PrivateAssets="All" />
  </ItemGroup>

  <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
    <ItemGroup>
      <BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
    </ItemGroup>
  </Target>
</Project>
```

| Manifest fact | Value | Source |
| --- | --- | --- |
| Project SDK | `Microsoft.NET.Sdk` | `src/SAIF.Premium.Api.Client/SAIF.Premium.Api.Client.csproj:1` |
| nuspec present | No | `src/SAIF.Premium.Api.Client/` and `scripts/` searched |
| Custom packing target | `CopyProjectReferencesToPackage` | `src/SAIF.Premium.Api.Client/SAIF.Premium.Api.Client.csproj:24` |

## Dependencies

| Dependency | Version | Purpose |
| --- | --- | --- |
| `SAIF.NetCore.ServiceExtensions` | 8.2.1 | `IHttpJsonClient` and `IEnvironmentClient` service surface |
| `SAIF.Http.ResponseValidator` | 8.0.3 | `HttpResponseValidator.ValidateHttpResponse` |
| `Microsoft.Extensions.DependencyInjection.Abstractions` | 8.0.1 | `IServiceCollection` and `AddTransient` for the `AddPremiumClient()` registration extension |

`Microsoft.Extensions.DependencyInjection.Abstractions` is the one dependency
beyond the SAIF HTTP and service packages. It is carried into every consumer, but
every consumer is a .NET 8 host that already references it, so it adds no
version constraint in practice.

See [Dependencies](dependencies.md) for the full dependency landscape including transitive and build-time references.

## Bundled project references

| Project reference | Metadata | Bundled into `lib/<tfm>` | Emitted as package dependency | Source |
| --- | --- | --- | --- | --- |
| `..\SAIF.Premium.Api.Contracts\SAIF.Premium.Api.Contracts.csproj` | `PrivateAssets="All"` | Yes (`.dll` and `.pdb`) | No | `src/SAIF.Premium.Api.Client/SAIF.Premium.Api.Client.csproj:19` |

`PrivateAssets="All"` keeps `dotnet pack` from emitting a dependency on
`SAIF.Premium.Api.Contracts`, which no feed publishes, and the
`CopyProjectReferencesToPackage` target copies its output into `lib/net8.0`
instead. Consumers therefore receive `PremiumSummary`, `PremiumEstimate` and the
other contracts inside this package and cannot take a different version of them.

## Shipped content

| Source | Target | Content |
| --- | --- | --- |
| `bin/Release/net8.0/SAIF.Premium.Api.Client.dll` | `lib/net8.0/SAIF.Premium.Api.Client.dll` | Compiled client |
| `bin/Release/net8.0/SAIF.Premium.Api.Client.pdb` | `lib/net8.0/SAIF.Premium.Api.Client.pdb` | Debug symbols (via `AllowedOutputExtensionsInPackageBuildOutputFolder`) |
| `SAIF.Premium.Api.Contracts.dll` | `lib/net8.0/SAIF.Premium.Api.Contracts.dll` | Bundled contracts assembly (via `BuildOutputInPackage`) |
| `SAIF.Premium.Api.Contracts.pdb` | `lib/net8.0/SAIF.Premium.Api.Contracts.pdb` | Bundled contracts symbols |

No source files, content files or build assets are packed.

## Build and release pipeline

| Attribute | Value |
| --- | --- |
| Pipeline | `pipelines/PremiumApi_Client.yml` |
| Build variables | `pipelines/build-vars.yml` (`AppType` = `CoreApi`, `AppName` = `PremiumApi`, `MajorVersion` = `8`) |
| Build number format | `$(MajorVersion).$(Year:yyyy).$(Month)$(DayOfMonth).$(rev:r)` |
| Shared template | `/Projects/Framework/build-and-release-nuget.yml@templates` |
| Template parameters | `nugetTargetFolder` = `$(appName)`, `nugetPackageSuffix` = `branchName` |
| Project packed | Not determined from source |

The shared template selects the project to pack; no template parameter names it.
`AppType` is `CoreApi` because the repository's primary application is the Premium
API, not because this package is an API client. `nugetPackageSuffix: branchName`
means a branch build is packed as a pre-release with the branch name as its
suffix, so consumers see one package version per branch build.

## Feed

| Field | Value |
| --- | --- |
| Feed | Not determined from source |
| Publish trigger | Not determined from source |

## Related documentation
- [Client index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Client reference](reference.md)
- [Endpoint map](endpoint-map.md)
- [Consumption](consumption.md)
- [Contracts](contracts.md)
- [Dependencies](dependencies.md)
- [Business rules](business-rules.md)
- [Tech debt](tech-debt.md)

## Sources
- `src/SAIF.Premium.Api.Client/SAIF.Premium.Api.Client.csproj`
- `pipelines/PremiumApi_Client.yml`
- `pipelines/build-vars.yml`
````
