Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .agents/skills/purview-architecture-testing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ license: MIT
metadata:
author: Purview-Dev
version: "2.0"
repository: purview-dev/engineering-playbook
---

# Purview architecture testing
Expand Down Expand Up @@ -83,7 +84,7 @@ debugging the package.

Then add a test that the documentation matches the policy, so the table in the wiki is falsifiable
too. Full worked example:
[Project Responsibilities](https://github.com/purview-dev/engineering-playbook/blob/main/docs/wiki/Project-Responsibilities.md).
[Project Responsibilities](https://github.com/purview-dev/engineering-playbook/blob/main/docs/wiki/foundations/Project-Responsibilities.md).

## 3. Read project files, and assemblies

Expand Down Expand Up @@ -180,4 +181,4 @@ project classified so every dependency rule still applies to it. Prefer a waiver

- `purview-architecture` — what the projects are for, and how they are named
- `purview-buildsdk-architecture-tests` — the package's own skill, authoritative on its API
- [Architecture as Tests](https://github.com/purview-dev/engineering-playbook/blob/main/docs/wiki/Architecture-As-Tests.md)
- [Architecture as Tests](https://github.com/purview-dev/engineering-playbook/blob/main/docs/wiki/foundations/Architecture-As-Tests.md)
3 changes: 2 additions & 1 deletion .agents/skills/purview-architecture/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ license: MIT
metadata:
author: Purview-Dev
version: "1.0"
repository: purview-dev/engineering-playbook
---

# Purview architecture
Expand Down Expand Up @@ -173,4 +174,4 @@ small telemetry — it does not get none. See `purview-telemetry`.
- `purview-telemetry` — instrumentation at every scale
- `purview-persistence` — storage and GUID-key guidance, only when persistence is real
- [Engineering Principles](https://github.com/purview-dev/engineering-playbook/blob/main/docs/wiki/Engineering-Principles.md)
- [Adopting This At Your Scale](https://github.com/purview-dev/engineering-playbook/blob/main/docs/wiki/Adopting-This-At-Your-Scale.md)
- [Adopting This At Your Scale](https://github.com/purview-dev/engineering-playbook/blob/main/docs/wiki/governance/Adopting-This-At-Your-Scale.md)
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ combines" stays a review obligation, because a dependency matrix cannot express

## See also

- [Contracts and Data Exchange](https://github.com/purview-dev/engineering-playbook/blob/main/docs/wiki/Contracts-And-Data-Exchange.md)
- [Contracts and Data Exchange](https://github.com/purview-dev/engineering-playbook/blob/main/docs/wiki/data/Contracts-And-Data-Exchange.md)
— `Create` and `Hydrate`, and the schema as the source of truth for a wire shape
- [zodsharp](https://github.com/purview-dev/zodsharp) — authoritative on `[ZodSchema]` and JSON Schema
export
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The catalogue in brief. The authoritative definition is
[`.config/architecture-policy.json`](https://github.com/purview-dev/engineering-playbook/blob/main/.config/architecture-policy.json),
and the full page — with diagrams — is
[Project Responsibilities](https://github.com/purview-dev/engineering-playbook/blob/main/docs/wiki/Project-Responsibilities.md).
[Project Responsibilities](https://github.com/purview-dev/engineering-playbook/blob/main/docs/wiki/foundations/Project-Responsibilities.md).
Do not restate either here or in another skill; link to them.

## The catalogue
Expand Down
11 changes: 8 additions & 3 deletions .agents/skills/purview-buildsdk-adoption/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,14 @@ silently gets no runner, no coverage, no `InternalsVisibleTo` and no category.

## 5. Agent skills are a separate, explicit step

The SDK does not install agent skills, and nothing it does during a build writes to an agent
directory. It reads which skills each referenced package ships and recommends the command that
installs them; the skills CLI does the installing. See `purview-buildsdk-skill-installation`.
Nothing the SDK does during a restore or a build touches agent skills. Installing, updating and
cleaning them up are separate targets a person runs by name — `InstallPurviewSkills`,
`UpdatePurviewSkills`, `CheckPurviewSkills`, `FixPurviewSkills` — and the ones that change the
repository refuse to run inside a build (`PRSKL0009`). Do not hook them into one. The skills CLI does
the installing, through `bun x` by default; with `PurviewSkillsAgents` unset it auto-detects agents.

If the repository uses `just`, add recipes for them — see `purview-buildsdk-skills-justfile`. For what each
target does and the `PRSKL*` diagnostics, see `purview-buildsdk-skill-installation`.

## Constraints

Expand Down
25 changes: 23 additions & 2 deletions .agents/skills/purview-buildsdk-configuration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ in `Directory.Build.props` unless the setting is deliberately project-specific.
| `TestingFramework` | `TUnit` | `TUnit`, `Xunit` or `None` |
| `SubstituteFramework` | `TUnitMocks` | `TUnitMocks`, `NSubstitute` or `None` |
| `TestDataFramework` | `Bogus` | `Bogus` or `None` |
| `TargetFramework` | `net10.0` | `netstandard2.0` when `IsRoslynComponent=true` |
| `TargetFramework` | `net10.0;net11.0` | `netstandard2.0` when `IsRoslynComponent=true` |
| `IsPackable` | `false` | Opts a project into the packable defaults |
| `UsePackageJsonVersion` | `true` | `true`, `false` or `Strict` — version from `package.json` |
| `ExcludePurviewTelemetry` | `false` | Drops the telemetry source generator |
Expand All @@ -50,6 +50,11 @@ in `Directory.Build.props` unless the setting is deliberately project-specific.
| `PurviewBrowserSafe` | *(unset)* | On a class library a WebAssembly client downloads: trim, AOT and browser-unsupported APIs fail the build |
| `IsBffProject` | name ends `Bff` | Classifies a BFF host; `false` opts out |
| `PurviewBrowserSafeReferenceCheckAsError` | `false` | Makes `PRBLZ0001` (a client reference that is not browser-safe) an error |
| `MemorySafetyRules` | `2` | C# 15 memory-safety tier on `net11.0`+: `0` off, `1` preview syntax, `2` updated rules |
| `PurviewRuntimeAsync` | `true` | `runtime-async=on` on `net11.0`+ |
| `IsClientSideProject` | *(computed)* | Whether the project's assemblies can reach a browser; overrides the `ServerShared` classification |
| `PurviewAutoServerSharedReference` | `true` | Adds a sibling `ServerShared` reference to a non-client-side project |
| `PurviewMultiThreadedBuild` | `true` | Bootstraps a `Directory.Build.rsp` enabling MSBuild `-mt` |

The full set, grouped by area and with the behaviour each one changes, is in
[references/properties.md](references/properties.md). Read it rather than guessing a property name —
Expand Down Expand Up @@ -89,11 +94,15 @@ is the SDK's answer, and it stays correct when the rules change. The exported se

## Repository bootstrap

The SDK bootstraps two repository-wide files from the package: `.editorconfig` and `global.json`.
The SDK bootstraps three repository-wide files from the package: `.editorconfig`, `global.json` and
`Directory.Build.rsp`.

- `PurviewRepoBootstrapMode` — `IfMissing` (default), `Always`, `WarnOnDrift`, `Never`
- `DisableAutoCopySdkFiles` — the master off switch
- `PurviewRepoBootstrapCopyRetries` / `...RetryDelayMilliseconds` / `...FailureAsError`
- `BootstrapMultiThreadedBuildRspToRepoRoot` / `PurviewMultiThreadedBuild` — the response file that
enables MSBuild `-mt` (and `-nr:false`, which avoids a `RoslynCodeTaskFactory` cache left by a
non-`-mt` build)

Every project in a solution writes those same destinations, so the write is race-tolerant by
design: an existing file is never overwritten unless asked, each write is staged into a temporary
Expand All @@ -105,6 +114,18 @@ the repository; that is gone, along with `EnableAgentFolderInPackage`, `AgentPac
and the `PurviewAgentFolder*` properties. Skills have exactly one installer, the skills CLI — see
`purview-buildsdk-skill-installation`.

The skill targets read their settings when they run, so these can go in `Directory.Build.props` or
on the command line, and a repository-wide choice belongs in `Directory.Build.props`:

| Property | Default | Controls |
| -- | -- | -- |
| `PurviewSkillsRunner` | `bun` (`bun x skills`) | Which package runner launches the skills CLI |
| `PurviewSkillsAgents` | empty — the CLI auto-detects | Agents to install to; when set, the only agent directories `CheckPurviewSkills` allows |
| `PurviewSkillsDryRun` | `false` | Install and update print commands instead of running them |

On a command line, `dotnet msbuild -p:` splits values on `,` and `;` — write
`-p:PurviewSkillsAgents=claude-code%2Ccodex`. For `just` recipes, see `purview-buildsdk-skills-justfile`.

## Guidance when changing SDK configuration

1. Preserve existing defaults unless the task explicitly changes product behaviour.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ consuming project supplies nothing.

| Property | Default | Behaviour |
| -- | -- | -- |
| `TargetFramework` | `net10.0` | Applied when neither `TargetFramework` nor `TargetFrameworks` is set. A project that explicitly declares `IsRoslynComponent=true` defaults to `netstandard2.0` |
| `TargetFramework` | `net10.0;net11.0` | Applied when neither `TargetFramework` nor `TargetFrameworks` is set. A project that explicitly declares `IsRoslynComponent=true` defaults to `netstandard2.0` |
| `IsRoslynComponent` | *(unset)* | When explicitly `true`: a single `netstandard2.0` target, `LangVersion=latest`, `Nullable=enable`, `TreatWarningsAsErrors=true`, `Deterministic=true`, extended analyzer rules, SourceLink with `EmbedUntrackedSources=true`, no dependency file, telemetry exclusion, and `PrivateAssets=all` on `Microsoft.CodeAnalysis.*` references |
| `IsRoslynComponentOnly` | `true` for Roslyn components | Analyzer-only package: `IncludeBuildOutput=false`, `IncludeSymbols=false`, portable PDB packed beside the analyzer under `analyzers/dotnet/cs/`. Set `false` for a dual-role component using normal library symbol packaging |
| `DisableRoslynCompilerDefaultsValidation` | `false` | Opts out of `ValidateRoslynComponentCompilerSettings`, which fails the pack of a packable Roslyn component missing `LangVersion`, `Nullable`, `TreatWarningsAsErrors` or `EnforceExtendedAnalyzerRules` |
Expand All @@ -36,6 +36,19 @@ consuming project supplies nothing.
| `DisablePurviewTestContextRuleSet` | `false` | Makes test and shared-testing projects enforce the production API-surface rules too |
| `PurviewPolicyExemptNoWarn` | *(derived)* | The SDK-injected `NoWarn` entries `ValidatePurviewStylePolicy` accepts |

## .NET 11 features and ServerShared

| Property | Default | Behaviour |
| -- | -- | -- |
| `MemorySafetyRules` | `2` | C# 15 memory-safety tier on `net11.0`+ targets: `0` off, `1` preview pointer syntax (sets `LangVersion=preview` when none was chosen), `2` `updated-memory-safety-rules`. Unknown values fail with `PRCFG0001`. Never touches `AllowUnsafeBlocks` |
| `PurviewRuntimeAsync` | `true` | `runtime-async=on` on `net11.0`+ targets. `UseRuntimeAsync=false` also opts out |
| `PurviewMultiThreadedBuild` | `true` | Bootstraps a managed `Directory.Build.rsp` with `-mt`/`-nr:false`; `false` writes `-mt:false` |
| `BootstrapMultiThreadedBuildRspToRepoRoot` | `true` | Whether that bootstrapped response file is written at all |
| `IsClientSideProject` | *(computed)* | `true` for a WebAssembly client, `PurviewBrowserSafe` library, Razor class library, or `*.Client`/`*.Browser`/`*.Wasm`/`*.WebAssembly`/`*.Components` name. Explicit value wins |
| `PurviewAutoServerSharedReference` | `true` | Adds `../ServerShared*/ServerShared*.csproj` to a non-client-side project |
| `PurviewServerSharedReferenceCheckAsError` | `false` | Makes `PRSHR0001`/`PRSHR0002` errors |
| `DisablePurviewServerSharedReferenceCheck` | `false` | Turns `PRSHR0001`/`PRSHR0002` off |

## Testing

| Property | Default | Behaviour |
Expand Down Expand Up @@ -119,6 +132,9 @@ reference was judged browser-safe when it cannot say so itself.
| `RepositoryEditorConfigFilePath` | *(derived)* | Overrides the destination `.editorconfig` |
| `BootstrapGlobalJsonToRepoRoot` | `true` | |
| `RepositoryGlobalJsonFilePath` | *(derived)* | Overrides the destination `global.json` |
| `BootstrapMultiThreadedBuildRspToRepoRoot` | `true` | Creates a managed `Directory.Build.rsp` enabling `-mt` |
| `RepositoryDirectoryBuildRspFilePath` | *(derived)* | Overrides the destination `Directory.Build.rsp` |
| `PurviewMultiThreadedBuild` | `true` | `false` rewrites a managed rsp to `-mt:false` |
| `PurviewBuildSdkVersionForGlobalJson` | restored package version, else the version it was packed as | |
| `PurviewRepoBootstrapCopyRetries` | `3` | |
| `PurviewRepoBootstrapCopyRetryDelayMilliseconds` | `500` | |
Expand All @@ -136,7 +152,7 @@ nothing else was left for the mirror to deliver.
Covered in full by `purview-buildsdk-skill-installation`: `PurviewSkillsRunner`,
`PurviewSkillsRunnerCandidates`, `PurviewSkillsCommand`, `PurviewSkillsPackage`,
`PurviewSkillsAgents`, `PurviewSkillsSource`, `PurviewSkillsInstallRoot`, `PurviewSkillsRepoRoot`,
`EnablePurviewSkillRecommendations`, `PurviewSkillRecommendationsInCI`.
`PurviewSkillsDryRun`, and the `PurviewSkillsAgentDirectory` item group.

## Compiler-visible properties

Expand All @@ -148,7 +164,11 @@ Exported to analyzers and generators as `build_property.<PropertyName>`:
- `ExcludePurviewTelemetry`, `ExcludeMSTelemetryExtension`
- `EnableAssemblyNameGeneration`, `DisableAutoInternalsVisibleTo`, `DisableGenerateAssemblyInfoClass`,
`DisableGenerateAssemblyIdentifierClass`, `AssemblyIdentifierClassName`
- `IsCSharpProject`, `IsTestProject`, `IsSharedTestingProject`, `IsSharedProject`
- `IsCSharpProject`, `IsTestProject`, `IsSharedTestingProject`, `IsSharedProject`,
`IsServerSharedProject`, `IsClientSideProject`
- `PurviewAutoServerSharedReference`, `DisablePurviewServerSharedReferenceCheck`,
`PurviewServerSharedReferenceCheckAsError`
- `MemorySafetyRules`, `PurviewRuntimeAsync`, `PurviewMultiThreadedBuild`
- `TestingType`, `TargetProjectName`
- `IsContainerProject`, `IsSdkProject`, `SdkProjectName`, `IsWebProject`, `IsWebSdkProject`,
`IsWorkerSdkProject`, `IsAspireHostProject`, `IsCLIProject`
Expand All @@ -163,6 +183,6 @@ Exported to analyzers and generators as `build_property.<PropertyName>`:
- `PurviewSuppressCopyRetryWarnings`
- `PurviewSkillsSourceFolder`,
`PurviewSkillsAgents`, `PurviewSkillsInstallRoot`, `PurviewRepositoryRootMarkers`,
`PurviewSkillsRunner`
`PurviewSkillsRunner`, `PurviewSkillsDryRun`
- `PurviewBuildSdkVersionForGlobalJson`, `CurrentYear`, `AutoGeneratedAssemblyInfoFile`,
`AutoGeneratedAssemblyIdentifierFile`
16 changes: 11 additions & 5 deletions .agents/skills/purview-buildsdk-project-detection/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: purview-buildsdk-project-detection
description: Explain why Purview.BuildSdk classified a project as test, shared, shared-testing, CLI, web, worker, Aspire host, container, Roslyn component, Blazor WebAssembly client, Razor class library, Blazor host or BFF, and why it added a package, a project reference, a namespace or an analyzer automatically. Use when SDK behaviour looks surprising, when a test project gets no runner or no InternalsVisibleTo, or when reasoning about RootNamespace, AssemblyName and TargetProjectName.
description: Explain why Purview.BuildSdk classified a project as test, shared, shared-testing, ServerShared, client-side, CLI, web, worker, Aspire host, container, Roslyn component, Blazor WebAssembly client, Razor class library, Blazor host or BFF, and why it added a package, a project reference, a namespace or an analyzer automatically. Use when SDK behaviour looks surprising, when a test project gets no runner or no InternalsVisibleTo, or when reasoning about RootNamespace, AssemblyName and TargetProjectName.
license: MIT
metadata:
package: Purview.BuildSdk
Expand All @@ -25,7 +25,8 @@ dotnet msbuild <project> -getProperty:PurviewLogicalProjectName -getProperty:Roo

The values worth inspecting first: `MSBuildProjectName`, `NamespacePrefix`,
`PurviewLogicalProjectName`, `RootNamespace`, `TestingType`, `TargetProjectName`, `SdkProjectName`,
`IsTestProject`, `IsSharedProject`, `IsSharedTestingProject`, `IsContainerProject`, `IsCLIProject`,
`IsTestProject`, `IsSharedProject`, `IsServerSharedProject`, `IsClientSideProject`,
`IsSharedTestingProject`, `IsContainerProject`, `IsCLIProject`,
`IsWebSdkProject`, `IsAspireHostProject`, `IsBlazorWebAssemblyProject`, `IsRazorClassLibraryProject`,
`IsBlazorHostProject`, `IsBffProject`.

Expand All @@ -52,11 +53,16 @@ a non-test project grants access to `{AssemblyName}.{TestType}Tests` for each na
suffix outside it still builds, runs and gets a category — it just cannot see `internal` members.
The 53 names are in [references/test-types.md](references/test-types.md).

### Shared and shared-testing projects
### Shared, shared-testing and ServerShared projects

Recognised by **exact name**, never by substring. The two lists are in
Recognised by **exact name**, never by substring. The three lists are in
[references/detection.md](references/detection.md). Derived: `IsSharedProject`,
`IsSharedTestingProject`.
`IsSharedTestingProject`, `IsServerSharedProject`.

`ServerShared` is separate from `Shared` for a reason: a `Shared` project is referenced by almost
everything, including code that ends up in a browser, so server-only helpers get their own name and
are referenced automatically only by a project that cannot be downloaded. `IsClientSideProject` is the
classification that decides it.

### SDK and content based

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@
`Shared`, `SharedFramework`, `SharedInfrastructure`, `SharedInfra`, `SharedUtilities`,
`SharedUtils`, `SharedLibrary`, `SharedLib`, `SharedHelpers`

## ServerShared project names (exact)

`ServerShared`, `ServerSharedFramework`, `ServerSharedInfrastructure`, `ServerSharedInfra`,
`ServerSharedUtilities`, `ServerSharedUtils`, `ServerSharedLibrary`, `ServerSharedLib`,
`ServerSharedHelpers`

A `ServerShared` project is referenced automatically by every non-client-side project, and never by a
WebAssembly client, a `PurviewBrowserSafe` library, a Razor class library, a project named with a
client-side segment (`*.Client`, `*.ClientShared`, `*.Browser`, `*.Wasm`, `*.WebAssembly`,
`*.Components`, or exactly `Components`/`ClientShared`), or a `Shared`/`SharedTesting` project.
`IsClientSideProject` overrides the classification. `PRSHR0001` reports a client-side reference and
`PRSHR0002` a `Shared` reference; `PurviewBrowserSafe="true"` on the reference records an accepted
decision.

## Shared testing project names (exact)

`SharedTestingFramework`, `SharedTestingInfrastructure`, `SharedTestingInfra`,
Expand Down Expand Up @@ -64,7 +78,8 @@ dual-role component that wants normal library symbol packaging.
| Container | `InvariantGlobalization`, `PublishAot`, Linux Docker defaults, container tooling packages |
| Web SDK | `Microsoft.AspNetCore.OpenApi.Generated` added to `InterceptorsNamespaces`, unless marked as a separate web-project mode |
| Aspire host | `OutputType=Exe` |
| Blazor WebAssembly client | not packable, no container defaults, no automatic `Shared` reference, the browser-safe reference check - see [blazor.md](blazor.md) |
| Blazor WebAssembly client | not packable, no container defaults, no automatic `Shared`/`ServerShared` reference, the browser-safe reference check - see [blazor.md](blazor.md) |
| ServerShared project | referenced automatically by every non-client-side project; never by a WebAssembly client, browser-safe library, Razor class library, client-side name, or `Shared`/`SharedTesting` project |
| Blazor host | an automatic reference to its `<Name>.Client` - see [blazor.md](blazor.md) |
| BFF | no packages, no OpenAPI interceptor unless it exposes an API, `PDS0008`/`PDS0009` - see [blazor.md](blazor.md) |
| `PurviewBrowserSafe` library | `IsAotCompatible`, `SupportedPlatform browser`, trim/AOT/`CA1416` as errors - see [blazor.md](blazor.md) |
Expand Down
Loading
Loading