Repository navigation
Feat/zodsharp 3 blazor - #23
Merged
Merged
Conversation
…rp 3.0.0-prerelease.1 SourceGeneratorFramework 1.2.2 stops its framework assembly leaking into the build output of projects that reference a generator as an analyzer, so the in-repo samples and tests no longer carry Purview.SourceGeneratorFramework.dll in their bin folders. Purview.ZodSharp 3 ships net10.0 and net11.0 only, so the two ZodSharp samples move from the repository's 'All' set to 'Supported'. Every other ZodSharp consumer in the repository already targeted 'Supported'. The runtime package does not depend on ZodSharp and keeps 'All'. ZodSharp 3 breaks surfaced by the tests: - built-in rules now report a structured origin and category, so the NonSentinel test asserts origin 'string' and category 'invalid_value' instead of no origin - generated schemas gain ValidateAsync, so CA1849 flags the synchronous Validate in an async test; it now awaits ValidateAsync
The generated scalar converter read and wrote its underlying value through
JsonSerializer.Deserialize<T>(ref reader, options) and JsonSerializer.Serialize(writer, value,
options), and the complex converter round-tripped a generated {Name}JsonModel class the same way.
Those overloads are [RequiresUnreferencedCode]/[RequiresDynamicCode], so every trimmed or Native
AOT consumer - Blazor WebAssembly included - got IL2026/IL3050 from generated code, and the model
class needed metadata no source-generated context could list. Nothing in the repository analyzed
generated code under trimming, so it went unnoticed; the new packed-consumer gate found it.
The converters now go through ValueObjectJson, a new runtime helper:
- the options' own metadata when they have it, so converters, naming policies and number
handling behave exactly as before
- the generated converter of a value-object-typed member, which a source-generated context in
the declaring project cannot see because one generator never sees another's output
- the built-in System.Text.Json converter for a primitive the context never listed
- otherwise a NotSupportedException naming the type to add with [JsonSerializable]
The complex converter reads and writes members directly, honouring PropertyNamingPolicy,
PropertyNameCaseInsensitive and DefaultIgnoreCondition, skipping unknown members and defaulting
missing ones, as the model class did. The public {Name}JsonModel nested type is no longer emitted.
BuildSdk 1.2.0 rewrote the managed .editorconfig with its new version marker; the rule set is unchanged. It added Blazor WebAssembly, Razor class library, BFF and Blazor host detection, PurviewBrowserSafe with the PRBLZ0001 reference check, PDS0007-PDS0009 and the unmarked .editorconfig warning. None of them applies to a project in this repository - there is no Blazor project in the solution, and the packed-consumer gate builds its Blazor apps outside the SDK - and the build produced no new warning. Skills.Testing 1.2.0 passes every skill unchanged.
…sembly and Blazor Server Every in-repo build references the generator and runtime as projects, which exercises nothing of what ships. Modelled on purview-dev/zodsharp's gate, `just smoke-packed-consumers` packs Purview.ValueObjects to an isolated feed and NUGET_PACKAGES, guards Directory.Build.* and Directory.Packages.props in a temp root, and builds consumers outside the repository from the package alone, with Purview.ZodSharp 3: - a Native AOT console over scalars, a complex value object, [ZodSchema] DTOs and System.Text.Json source generation, published with -warnaserror and run - a Blazor WebAssembly app with a <ZodValidator> form and a <DataAnnotationsValidator /> form over a model of value objects, published trimmed and with RunAOTCompilation=true, served and run in a headless browser driven over the DevTools protocol - a Blazor Server app whose statically rendered form is posted invalid and valid, and whose interactive page runs the same self-test over a live circuit - a net8.0 console, so the runtime's oldest target framework keeps restoring and running Every bin and publish folder is scanned for compiler-host assemblies, including the value object generator and code fix, by scripts/assert-no-compiler-host-assemblies.ps1. Purview.ZodSharp prerelease gaps are reported as known issues rather than failures (-StrictKnownIssues fails on them), and the WebAssembly AOT publish leaves out the type-level rule that crashes the Mono AOT compiler while a separate probe keeps checking it. PackValidation gains ForbiddenContent rules mirroring ZodSharp's, and `just verify-pack-rules` (scripts/test-pack-validation-rules.ps1) proves each of the 25 rules rejects a package.
… requests Purview.ZodSharp 3.0.0-prerelease.1 and its Blazor package are built against the rc.3 ASP.NET Core packages, and the WebAssembly consumers need the matching rc.3 WebAssembly SDK, so global.json and both workflows move from rc.2 to rc.3 together. The new packed-consumers job runs scripts/test-packed-consumers.ps1 on ubuntu-latest with -WasmAot Require and -Browser Require (the image ships Chrome), then proves every pack-validation ForbiddenContent rule. Purview.SourceGeneratorFramework 1.2.2 and Purview.ZodSharp 3.0.0-prerelease.1 are not on nuget.org yet, so until they are - or the VALUEOBJECTS_DEPENDENCY_FEED repository variable names a feed that has them - the job fails at its first step, the dependency preflight, with what to do, instead of skipping.
…grade A new guide, docs/Trimming-AOT-and-Blazor.md, covers the target frameworks (the runtime keeps net8.0-net11.0; ZodSharp 3 needs net10.0+), the generated JSON converters under trimming, registering generated converters on a source-generated JsonSerializerContext declared in the same project, binding a value object in a Blazor form, validating it with Purview.ZodSharp.Blazor, and the Purview.ZodSharp 3.0.0-prerelease.1 gaps the packed-consumer gate found. The README, the package README, the ZodSharp guide, both affected skills and AGENTS.md point at it, and AGENTS.md names `just smoke-packed-consumers` as the proof that the generated path is trim- and AOT-clean.
A minor prerelease: no target framework is dropped, and the public API only grows (ValueObjectJson).
The generated complex converter no longer emits its public {Name}JsonModel nested type, which is
generated into consumer code rather than shipped in the package.
The changelog's former "Unreleased" section shipped across 1.2.0-1.2.3, so it is now headed that way.
…field placement
Purview.ZodSharp 3.0.0-prerelease.1 now compiles a type-level [ZodRule] under the Mono AOT compiler
and places an error inside a value object on the owning member's field, so the gate stops
accommodating either.
The WebAssembly AOT publish builds the same model as every other publish, PlanId's [NonSentinel]
included; the EXCLUDE_TYPE_LEVEL_RULES define, the ExcludeTypeLevelRules property and the separate
crash probe are gone. The self-test's KNOWN lines are ordinary checks: each value object's message
sits on its own field and names it ("Field 'Email' must be a valid email address."), the type-level
rule's message sits on Plan, and no message names Value. The static Blazor Server post asserts the
same placement. The known-issue reporting and -StrictKnownIssues are removed, so every check fails
the gate.
…console The Native AOT console's Order declared ContactEmail as a non-nullable EmailAddress only because Purview.ZodSharp's generated null test for a [Required] nullable struct bound the scalar's operator ==(string?, EmailAddress) and failed with CS9342. That is fixed, so the member is back to its natural shape, [Required] EmailAddress?. The console proves it compiles under -warnaserror and validates: a missing address is reported at ContactEmail with RequiredRule's code, and a present but invalid one is reported at ContactEmail.Value with a message that names the owning member.
The Trimming, Native AOT and Blazor guide, the CHANGELOG and the purview-value-objects-zodsharp skill listed Purview.ZodSharp 3.0.0-prerelease.1 defects found by the packed-consumer gate. All of them are fixed in ZodSharp, so the wording is gone. In its place the guide and the skill describe the behaviour the gate now asserts: an error inside a value object is shown on the owning member's field and named after it, a type-level rule is located at the member, and a [Required] nullable value object member is supported. The upgrade notes record the changed message text.
The skills this repository depends on are installed by the skills CLI into the universal
.agents/skills/ location and committed with skills-lock.json, so a clone has them without
running anything. Nothing was written outside .agents/ and skills-lock.json:
npx -y skills@1 add purview-dev/build-sdk -s '*' -a universal -y
npx -y skills@1 add purview-dev/sourcegenerator-framework -s '*' -a universal -y
npx -y skills@1 add purview-dev/zodsharp -s '*' -a universal -y
That is the eight purview-buildsdk-*, six purview-sourcegenerator-* and six purview-zodsharp-*
skills. This repository's own skills stay in skills/.
/.claude/ is gitignored, and src/Directory.Build.props sets PurviewSkillsAgents to universal, so
the SDK's InstallPurviewSkills target no longer defaults to writing a copy for every agent.
…ention InstalledSkillTests checks that every skills-lock.json entry is installed under .agents/skills/, that every installed folder is recorded in the lockfile, that no entry is one of this repository's own skills, and that neither a .claude/skills/ copy nor a CLAUDE.md exists.
…ntion AGENTS.md gains an "Installed skills (.agents/skills/)" section: the installed set and its sources, the -a universal install and restore commands, why nothing goes into .claude/ and no CLAUDE.md is added, and the test that guards it. The authored-skills section no longer says .agents/skills/ is gitignored, and the layout table lists both skill folders.
Every purview repository installs purview-dev/engineering-playbook beside purview-dev/build-sdk.
Installed the same way as the others, into the universal location only, and nothing was written
outside .agents/ and skills-lock.json:
npx -y skills@1 add purview-dev/engineering-playbook -s '*' -a universal -y
That adds purview-architecture, purview-architecture-testing, purview-engineering-principles,
purview-persistence, purview-telemetry and purview-testing-conventions.
InstalledSkillTests now mirrors ZodSharp's five checks. A skill authored in skills/ must not appear in .agents/skills/, whether or not the lockfile records it, and the CLAUDE.md check also covers .github/CLAUDE.md.
The installed-skills table and install commands include purview-dev/engineering-playbook, and the description of InstalledSkillTests matches its checks.
The packed-consumer gate's Blazor WebAssembly consumer had no wwwroot/index.html in the repository: the web section of .gitignore ignores *.html at any depth, so the page existed only on the machine that wrote it. A fresh CI checkout therefore published an app whose wwwroot held _framework but no index.html, the static file host's MapFallback threw FileNotFoundException sending it, and the gate failed with "The WebAssembly host answered 500 for /". Add the page, un-ignore the fixtures' wwwroot folders, and assert index.html in Assert-WasmOutput so a future loss is reported as a missing file rather than as a 500 from the host.
The pack-validation step failed with exit 1 because dotnet tool
restore reported
The command "purview-build" specified in the tool manifest file is
not contained in the package with Package Id purview.build. The
commands contained in the package are dotnet-inspect.
although Purview.Build 0.8.1 declares <Command Name="purview-build">
and restores correctly on its own. Reproduced on a cold package cache
with both 0.8.1 and 0.7.1: the .NET 11 rc.3 CLI attributes the command
of the RID-specific dotnet-inspect tool to the manifest entry beside it.
The failed attempt has downloaded and installed every tool by then, so
the next attempt succeeds.
scripts/restore-dotnet-tools.ps1 retries a bounded number of times. It
is quiet on success and prints every attempt's output when all fail, so
the retry cannot hide a genuine failure. It is now the only place local
tools are restored: `just` (ensure-pipeline-tool), the CI
pack-validation step, and the scratch repository that
test-pack-validation-rules.ps1 builds for each of its 27 runs.
Also give pr.yml its final newline.
GitHub's pwsh shell appends `if ((Test-Path variable:\LASTEXITCODE)) {
exit $LASTEXITCODE }` to the step, so the step's result is whatever the
last native command left behind - and
scripts/test-pack-validation-rules.ps1 ends with 27 `dotnet
purview-build` runs that must fail, so the step reported exit 1 after
printing "Every ForbiddenContent rule (25) rejected its file".
Exit 0 explicitly once every rule has passed, and clear $LASTEXITCODE
when a retried dotnet tool restore succeeds, so the retry cannot poison
a step that only restores tools.
Reproduced with the runner's wrapper locally: cold cache, attempt 1
fails, attempt 2 restores, 27 validation runs pass, step exit 0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.