ktsu.Semantics.Strings
3.0.0
Prefix Reserved
See the version list below for details.
dotnet add package ktsu.Semantics.Strings --version 3.0.0
NuGet\Install-Package ktsu.Semantics.Strings -Version 3.0.0
<PackageReference Include="ktsu.Semantics.Strings" Version="3.0.0" />
<PackageVersion Include="ktsu.Semantics.Strings" Version="3.0.0" />
<PackageReference Include="ktsu.Semantics.Strings" />
paket add ktsu.Semantics.Strings --version 3.0.0
#r "nuget: ktsu.Semantics.Strings, 3.0.0"
#:package ktsu.Semantics.Strings@3.0.0
#addin nuget:?package=ktsu.Semantics.Strings&version=3.0.0
#tool nuget:?package=ktsu.Semantics.Strings&version=3.0.0
ktsu.Semantics.Strings
Strongly-typed, self-validating string wrappers that replace primitive obsession with compile-time-safe domain types.
ktsu.Semantics.Strings is one package in the ktsu.Semantics family. For the family overview and the other pillars (paths, quantities, music, color) start at the root README.
Introduction
ktsu.Semantics.Strings gives you a base type, SemanticString<TDerived>, for defining string-shaped domain types such as EmailAddress, UserId, or BlogSlug. A semantic string validates itself on construction, normalizes its value, carries the whole System.String surface, and is distinct from every other semantic type at compile time. An EmailAddress will not silently substitute for a UserId, so a whole class of "passed the arguments in the wrong order" bugs stops compiling.
Validation is declarative. You attach attributes such as [IsEmailAddress] or [StartsWith("USER_")] to the type, and the framework runs them whenever an instance is created. Ready-made identifier types (Uuid, Iban, Isbn, and more) live in the companion ktsu.Semantics.Strings.Identifiers package.
Features
SemanticString<TDerived>base type: an abstract record using the curiously-recurring template pattern, so derived types get value equality, ordering, and the full string API for free.- Validating factories:
Createthrows on invalid input,TryCreatereturns a bool and never throws. Both acceptstring,char[], andReadOnlySpan<char>. - Declarative validation attributes: casing, format, text, and first-class .NET type checks, combined with
[ValidateAll](default, logical AND) or[ValidateAny](logical OR). - Normalization hook: override
MakeCanonicalto trim, case-fold, or otherwise canonicalize a value before validation runs. - Fluent conversions:
"user@example.com".As<EmailAddress>()and cross-type reinterpretation viasource.As<TSource, TTarget>(). - Factory abstraction for dependency injection:
ISemanticStringFactory<T>/SemanticStringFactory<T>for constructor injection, with aSemanticStringFactory<T>.Defaultsingleton for non-DI use. - Span-friendly and allocation-conscious: span-based overloads and a
ref structsplit enumerator on the target frameworks that support them. - JSON round-trip serialization: values serialize as their underlying string via
ktsu.RoundTripStringJsonConverter.
Installation
Package Manager Console
Install-Package ktsu.Semantics.Strings
.NET CLI
dotnet add package ktsu.Semantics.Strings
Package Reference
<PackageReference Include="ktsu.Semantics.Strings" Version="x.y.z" />
Usage Examples
Basic Example
using ktsu.Semantics.Strings;
[IsEmailAddress]
public sealed record EmailAddress : SemanticString<EmailAddress> { }
[StartsWith("USER_"), HasNonWhitespaceContent]
public sealed record UserId : SemanticString<UserId> { }
// Direct construction, no generic argument needed
EmailAddress email = EmailAddress.Create("user@example.com");
UserId userId = UserId.Create("USER_12345");
// Safe creation, no exception on failure
if (EmailAddress.TryCreate("maybe@invalid", out EmailAddress? safe))
{
// use safe
}
// Compile-time safety
public void SendWelcomeEmail(EmailAddress to, UserId who) { /* ... */ }
// SendWelcomeEmail(userId, email); // does not compile
A semantic string converts implicitly to string, so it drops into any API that expects one. Construction is always explicit (Create / As), which guarantees validation runs.
Combining attributes
// All attributes must pass (default behavior)
[IsEmailAddress, EndsWith(".com")]
public sealed record DotComEmail : SemanticString<DotComEmail> { }
// Any one attribute passing is sufficient
[ValidateAny]
[IsEmailAddress, StartsWith("https://")]
public sealed record ContactMethod : SemanticString<ContactMethod> { }
The parameterized text attributes (Contains, StartsWith, EndsWith, PrefixAndSuffix, RegexMatch) allow multiples, so you can stack several and combine them with [ValidateAny].
Normalization before validation
using ktsu.Semantics.Strings;
[HasNonWhitespaceContent]
public sealed record Slug : SemanticString<Slug>
{
protected override string MakeCanonical(string input) =>
input.Trim().ToLowerInvariant().Replace(' ', '-');
}
Slug slug = Slug.Create(" Hello World "); // stored as "hello-world"
Dependency injection
The package ships no AddSemanticStrings() helper. Register each closed factory type you need:
services.AddScoped<ISemanticStringFactory<EmailAddress>, SemanticStringFactory<EmailAddress>>();
public class UserService(ISemanticStringFactory<EmailAddress> emails)
{
public User CreateUser(string raw) =>
emails.TryFromString(raw, out EmailAddress? email)
? new User(email!)
: throw new ArgumentException("invalid email");
}
For code that is not using a container, SemanticStringFactory<EmailAddress>.Default is a ready singleton.
API Reference
SemanticString<TDerived>
Abstract base record for all semantic string types. TDerived is the concrete type itself.
Key members
| Name | Signature | Description |
|---|---|---|
WeakString |
string { get; init; } |
The underlying raw value. |
Length |
int { get; } |
Length of the underlying string. |
Create |
static TDerived Create(string?) (also char[], ReadOnlySpan<char>) |
Validates and constructs. Throws ArgumentException on invalid input, ArgumentNullException on null. |
TryCreate |
static bool TryCreate(string?, out TDerived?) (also char[], ReadOnlySpan<char>) |
Returns false instead of throwing. |
As<TDest>() |
TDest As<TDest>() |
Reinterprets the value as another semantic type, re-validating against its rules. |
MakeCanonical |
protected virtual string MakeCanonical(string) |
Normalization hook run before validation. |
IsValid |
virtual bool IsValid() |
True when the value is non-null and passes attribute validation. |
WithPrefix / WithSuffix |
TDerived WithPrefix(string) / TDerived WithSuffix(string) |
Type-safe prefix/suffix transforms. |
implicit string |
implicit operator string(SemanticString<TDerived>?) |
Converts to string (null becomes string.Empty). |
<, <=, >, >=, CompareTo |
ordering members | Ordinal comparison on the underlying string. |
The base also forwards the common System.String surface (Contains, IndexOf, Substring, Split, Trim, StartsWith, EndsWith, casing helpers, and more) plus span-based helpers on the target frameworks that support them.
ISemanticStringFactory<T> / SemanticStringFactory<T>
| Name | Return Type | Description |
|---|---|---|
FromString(string?) |
T |
Creates an instance, throwing on invalid input. |
FromCharArray(char[]?) |
T |
As above from a char array. |
TryFromString(string?, out T?) |
bool |
Non-throwing creation. |
SemanticStringFactory<T>.Default |
SemanticStringFactory<T> |
Shared singleton for non-DI use. |
Validation attributes
All attributes apply to a class, derive from SemanticStringValidationAttribute, and live in the ktsu.Semantics.Strings namespace.
| Category | Representative attributes |
|---|---|
| Casing | IsCamelCase, IsPascalCase, IsSnakeCase, IsKebabCase, IsMacroCase, IsTitleCase, IsSentenceCase, IsUpperCase, IsLowerCase |
| Format | HasNonWhitespaceContent, IsSingleLine, IsMultiLine, HasMinimumLines(n), HasMaximumLines(n), HasExactLines(n), IsEmptyOrWhitespace |
| Text | Contains(substring), StartsWith(prefix), EndsWith(suffix), PrefixAndSuffix(prefix, suffix), RegexMatch(pattern), IsBase64, IsEmailAddress |
| Combination markers | [ValidateAll] (default), [ValidateAny] |
The full catalogue lives in the validation reference.
Architecture
Validation is a small strategy/adapter/rule pipeline. A combination strategy (ValidateAllStrategy / ValidateAnyStrategy, chosen by ValidationStrategyFactory) decides whether a type's attributes are combined with AND or OR. Each attribute delegates to a ValidationAdapter that returns a ValidationResult. A separate rule abstraction (IValidationRule, ValidationRuleBase) provides an open extension point for adding named, prioritized rules without touching existing code. See the architecture guide for the full picture.
Contributing
Contributions are welcome! Feel free to open issues or submit pull requests.
License
This project is licensed under the MIT License. See the LICENSE.md file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- ktsu.RoundTripStringJsonConverter (>= 1.0.39)
- Microsoft.Bcl.AsyncInterfaces (>= 10.0.11)
- System.Memory (>= 4.6.3)
- System.Text.Json (>= 10.0.11)
- System.Threading.Tasks.Extensions (>= 4.6.3)
-
.NETStandard 2.1
- ktsu.RoundTripStringJsonConverter (>= 1.0.39)
- Microsoft.Bcl.AsyncInterfaces (>= 10.0.11)
- System.Text.Json (>= 10.0.11)
-
net10.0
- ktsu.RoundTripStringJsonConverter (>= 1.0.39)
- Microsoft.Bcl.AsyncInterfaces (>= 10.0.11)
-
net8.0
- ktsu.RoundTripStringJsonConverter (>= 1.0.39)
- Microsoft.Bcl.AsyncInterfaces (>= 10.0.11)
- System.Text.Json (>= 10.0.11)
-
net9.0
- ktsu.RoundTripStringJsonConverter (>= 1.0.39)
- Microsoft.Bcl.AsyncInterfaces (>= 10.0.11)
- System.Text.Json (>= 10.0.11)
NuGet packages (25)
Showing the top 5 NuGet packages that depend on ktsu.Semantics.Strings:
| Package | Downloads |
|---|---|
|
ktsu.AppDataStorage
A .NET library for persistent application data storage using JSON serialization. Provides a simple inherit-and-use pattern with automatic file management, thread-safe operations, debounced saves, backup recovery, and singleton access. Stores data in the user's app data folder with support for custom subdirectories and file names. |
|
|
ktsu.Semantics.Paths
A comprehensive .NET library for replacing primitive obsession with strongly-typed, self-validating domain models across four pillars: semantic strings with 50+ validation attributes, polymorphic path handling, metadata-generated semantic quantities, and musical value types. The quantity system covers 60+ physical dimensions and 200+ generated types under a unified vector model, with compile-time dimensional safety, generated unit conversions and physics relationships, centralized physical constants, and optional per-storage-type alias packages. The music types provide type-safe pitches, intervals, scales and modes, chords with symbol parsing and voicing, keys with roman-numeral analysis, and rational durations and time signatures. Features factory-pattern and dependency-injection support for building robust, maintainable scientific and domain-specific applications. |
|
|
ktsu.CredentialCache
A cross-platform credential cache for .NET that keeps secrets in memory for fast process-lifetime lookup and persists each one through the host's native keyring: Windows Credential Manager, macOS Keychain Services, or the freedesktop.org Secret Service on Linux. Every credential is stored as its own keyring entry scoped by a service name, so no plaintext blob is ever written to disk, and a pluggable ICredentialStore lets you substitute an in-memory store or your own backend. |
|
|
ktsu.ImGui.Popups
A professional library for modal dialogs and popup components in ImGui.NET, providing message boxes, input prompts with validation (string, int, float), searchable selection lists with type-safe generics, and an advanced filesystem browser with open/save modes, directory navigation, and pattern filtering support. |
|
|
ktsu.ImGuiCredentialPopups
A .NET library providing ready-made Dear ImGui modal dialogs for collecting credentials. Ships username/password and token popups built on a shared CredentialPopup base, with masked input, automatic keyboard focus, and confirmation callbacks that hand back a ktsu.CredentialCache credential ready to store or use. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.2.2 | 116 | 8/31/2026 |
| 3.2.1 | 390 | 8/28/2026 |
| 3.2.0 | 854 | 8/27/2026 |
| 3.1.4 | 930 | 8/25/2026 |
| 3.1.3 | 1,133 | 8/24/2026 |
| 3.1.2 | 929 | 8/21/2026 |
| 3.1.1 | 1,471 | 8/19/2026 |
| 3.1.0 | 516 | 8/19/2026 |
| 3.0.1 | 882 | 8/18/2026 |
| 3.0.0 | 1,270 | 8/15/2026 |
| 2.9.14 | 1,111 | 8/14/2026 |
| 2.9.13 | 177 | 8/14/2026 |
| 2.9.12 | 174 | 8/14/2026 |
| 2.9.11 | 461 | 8/14/2026 |
| 2.9.10 | 335 | 8/14/2026 |
| 2.9.9 | 163 | 8/14/2026 |
| 2.9.8 | 161 | 8/14/2026 |
| 2.9.7 | 190 | 8/14/2026 |
| 2.9.6 | 188 | 8/14/2026 |
| 2.9.5 | 169 | 8/14/2026 |
## v3.0.0 (major)
Changes since v2.0.0:
- [major] Sonar: clear all 24 open issues and the one security hotspot ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] MSTEST0058/0061: fix the two MSTest analyzer diagnostics ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S4136/S4144: group overloads and drop duplicate private factories ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S3776: reduce cognitive complexity in seven methods ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S3267: clear the last two generator loops ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S1172: drop the last four unused generator parameters ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S3267: simplify two generator loops with LINQ ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S1192: name the literals the generators emit ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Add a local SonarCloud check; simplify AdjustForContrast; test PatternValidationRule ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fix a sentence-case bug, harden the pattern regex, tidy the generator ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Extract the shared delimiter-separated casing rules ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Adopt ktsu.Sdk 2.27.0: KTSU0001 package references ([@matt-edmondson](https://github.com/matt-edmondson))
- Merge remote-tracking branch 'origin/main' into chore/sonarcloud-cleanup-2 ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Cover the untested casing and relative-path branches ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Cover PropertyTemplate shorthand; drop the duplicated const docs ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] S4136: group AsAbsolute overloads in the relative path types ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fix SonarCloud issues: S2223, S1192, S6610, S6580, S3358 ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fold the generator tests into Semantics.Test so their coverage is collected ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Add source generator tests to cover the generator pipeline ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] MSTEST0037: use the intent-revealing assertions ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] MSTEST0068: CollectionAssert.AreEqual -> Assert.AreSequenceEqual ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Migrate file headers to the one-line ktsu-dev form ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fix SonarCloud BLOCKER issues in test suite ([@matt-edmondson](https://github.com/matt-edmondson))
- Sync .serena\.gitignore ([@KtsuTools](https://github.com/KtsuTools))
- Sync .runsettings ([@KtsuTools](https://github.com/KtsuTools))
- Sync .editorconfig ([@KtsuTools](https://github.com/KtsuTools))
- Sync .gitattributes ([@KtsuTools](https://github.com/KtsuTools))
- Sync global.json ([@KtsuTools](https://github.com/KtsuTools))
- [minor] Return the root itself from AbsoluteDirectoryPath.Parent ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fix directory detection in SemanticRelativePath.Make ([@matt-edmondson](https://github.com/matt-edmondson))
- Make the new path tests platform-agnostic ([@matt-edmondson](https://github.com/matt-edmondson))
- Emit CRLF from the generators and re-enable package validation ([@matt-edmondson](https://github.com/matt-edmondson))
- [minor] Compare semantic strings by value and expose path values on interfaces ([@matt-edmondson](https://github.com/matt-edmondson))
- [minor] Add color adjustment operations across color spaces ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] Fix Oklch assertion in color cross-conversion tests ([@matt-edmondson](https://github.com/matt-edmondson))
- [minor] Add cross-space conversions between all color types ([@matt-edmondson](https://github.com/matt-edmondson))
- fix(music): use ValueTuple.GetHashCode instead of System.HashCode for netstandard2.0 compatibility ([@matt-edmondson](https://github.com/matt-edmondson))
- refactor(music): split Progression.TryParse into helpers to cut cognitive complexity and remove always-true check (SonarQube S3776/S2583) ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(music): update examples and references for Parse/TryParse rename and chart-style progressions ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): chart-style Arrangement ToString + Parse/TryParse + structural equality ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): chart-style Section ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music)!: replace bar-delimited Progression.Parse with chart-style ToString/Parse/TryParse + structural equality; migrate call sites ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): rename Form.FromPattern to Parse/TryParse, canonical ToString, structural equality ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Rest canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Note canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): ChordEvent canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): canonical Chord ToString, TryParse, ParseRoot via Notation ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Key canonical ToString + Parse/TryParse; roman-numeral accidental via Notation ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Scale canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Tempo canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Velocity canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): TimeSignature canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Duration canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): Interval canonical ToString + Parse/TryParse ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): rename Mode.FromName to Parse/TryParse, canonical ToString ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): typed Pitch factory, rename FromName to Parse/TryParse, canonical ToString ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): typed PitchClass factory, Parse/TryParse, canonical ToString ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add NoteLetter and Accidental enums ([@matt-edmondson](https://github.com/matt-edmondson))
- docs: implementation plan for music type-safe factories and canonical round-trip ToString ([@matt-edmondson](https://github.com/matt-edmondson))
- docs: revise music factories spec with canonical round-trip ToString and chart-style aggregate format ([@matt-edmondson](https://github.com/matt-edmondson))
- docs: design spec for music type-safe factories and Parse/TryParse convention ([@matt-edmondson](https://github.com/matt-edmondson))
- [patch] docs: add per-package READMEs and turn root README into a family index ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(music): document the analysis aggregate layer ([@matt-edmondson](https://github.com/matt-edmondson))
- test(music): lock guard/coverage contracts; dedupe chromatic scale check ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Form pattern extraction and named-form recognition ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Arrangement container ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Section structural unit ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add chromatic chord identification ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add key inference by diatonic fit ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(music): switch key inference to quality-weighted scoring ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add cadence detection ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add roman-numeral labeling and functional classification ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Progression.Parse bar-delimited chord syntax ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Progression core (construction, totals, empty rejection) ([@matt-edmondson](https://github.com/matt-edmondson))
- fix(music): drop CA1859 pragma; use IMusicalEvent helper in ChordEvent test ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add ChordEvent harmonic event type ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(music): implementation plan for analysis aggregate layer ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(music): design spec for analysis aggregate layer ([@matt-edmondson](https://github.com/matt-edmondson))
- test(strings): add As<T> round-trip test for Uuid ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): reconcile spec As<T> test bullet with implemented roster ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): document Identifiers package in README ([@matt-edmondson](https://github.com/matt-edmondson))
- chore(strings): finalize Identifiers package and document it ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add JwtToken identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add Iban identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add Isbn identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- style(strings): add trailing newline to IsCreditCardNumberAttribute.cs ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): use pattern-matching form in Tasks 5-6 (IDE0078) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add CreditCardNumber identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add Ulid identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): align plan Tasks 3-7 with repo conventions (ThrowsExactly, no using System, Ensure.NotNull) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(strings): add Uuid identifier type ([@matt-edmondson](https://github.com/matt-edmondson))
- chore(strings): scaffold Semantics.Strings.Identifiers package ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): correct empty-string handling in spec; add implementation plan ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(strings): spec for Semantics.Strings.Identifiers (Phase 0) ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(color): correct Oklab round-trip tolerance note in plan ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add NamedColors and gamma-regression tests ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add Oklab mix, lerp, distance, and gradient ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add WCAG luminance, contrast, and accessibility adjustment ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add HSL and HSV conversions ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add Oklab and Oklch perceptual spaces ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add hex and byte conversions (sRGB-interpreted) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): add Srgb space and gamma-correct sRGB<->linear boundary ([@matt-edmondson](https://github.com/matt-edmondson))
- docs(color): add semantic-domains roadmap, Semantics.Color spec and plan ([@matt-edmondson](https://github.com/matt-edmondson))
- style(color): strip UTF-8 BOM and add final newline (editorconfig) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(color): scaffold Semantics.Color with canonical linear Color type ([@matt-edmondson](https://github.com/matt-edmondson))
- docs: cover Semantics.Music score primitives, frequency, inversions, roman parsing ([@matt-edmondson](https://github.com/matt-edmondson))
- [minor] feat(music): score primitives, frequency bridge, inversions/transpose, roman-numeral parsing ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): parse roman numerals back into chords (inverse of RomanNumeralOf) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add chord inversions and Transpose on Chord/Scale/Key ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Pitch<->frequency (A440) and interval cents ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add score primitives (Velocity, Tempo, Note, Rest) with real-time conversion ([@matt-edmondson](https://github.com/matt-edmondson))
- Merge feature/semantics-music-types: musical value types ([@matt-edmondson](https://github.com/matt-edmondson))
- [minor] feat(music): musical value types (pitch, interval, scale, chord, key, duration) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Key with roman-numeral function; spell chromatic degrees conventionally (flat-preference) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Chord engine with parsing, tones, and voicing (full HeatDeathRomance vocabulary) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add TimeSignature with bar and beat durations ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add rational Duration with arithmetic and dotted/tuplet support ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Scale and ScaleDegree with degree resolution ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Mode with full standard scale catalog (diatonic, jazz, symmetric, pentatonic) ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Interval with octave folding and pitch difference ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): add Pitch with MIDI/name conversion and transpose ([@matt-edmondson](https://github.com/matt-edmondson))
- feat(music): scaffold Semantics.Music with PitchClass ([@matt-edmondson](https://github.com/matt-edmondson))
- fix(packaging): unblock the 2.0 release pipeline [patch] ([@matt-edmondson](https://github.com/matt-edmondson))