From 8f38db6da736de811e5b547ae8c7df9affe673a3 Mon Sep 17 00:00:00 2001 From: Chris Cavell Date: Fri, 18 Sep 2026 14:37:10 -0500 Subject: [PATCH] fix: align Learning 1.0 API validation with AsiBackbone 6.0 --- .../asibackbone-6-api-boundary.md | 6 ++- .../learning-1-asibackbone-6-compatibility.md | 6 ++- .../validate-asibackbone-6-api-references.cs | 54 +++++++++++-------- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/docs/getting-started/asibackbone-6-api-boundary.md b/docs/getting-started/asibackbone-6-api-boundary.md index 12acc72..2337d88 100644 --- a/docs/getting-started/asibackbone-6-api-boundary.md +++ b/docs/getting-started/asibackbone-6-api-boundary.md @@ -47,6 +47,7 @@ The names most often used by Learning material are: | Durable audit ledger | `AsiBackbone.Core.Audit.IGovernanceAuditLedgerStore` | | Actor context | `AsiBackbone.Core.Actors.GovernanceActorContext` | | ASP.NET Core endpoint service | `AsiBackbone.AspNetCore.Endpoints.IEndpointGovernanceService` | +| ASP.NET Core policy marker attribute | `AsiBackbone.AspNetCore.Endpoints.GovernancePolicyAttribute` | | Acknowledgment challenge service | `AsiBackbone.AspNetCore.Handshakes.IAcknowledgmentChallengeService` | These are implementation types. A Learning snippet that defines its own decision, receipt, context, or acknowledgment record is a teaching model unless the section explicitly says otherwise. @@ -107,6 +108,8 @@ app.MapPost("/exports", HandleExport) The marker records policy metadata. It does not, by itself, resolve a policy, select constraints, or enforce execution. See the exact [`EndpointGovernanceRouteBuilderExtensions`](https://github.com/AsiBackbone/AsiBackbone/blob/release/6.0.0/src/AsiBackbone.AspNetCore/Endpoints/EndpointGovernanceRouteBuilderExtensions.cs) behavior. +For attribute-based endpoint metadata, the 5.x `RequireGovernancePolicyAttribute` type was renamed to `GovernancePolicyAttribute` in 6.0. + ## Removed Compatibility Surface The upstream obsolete-member inventory found exactly seven public members whose compatibility window ended at 6.0: @@ -121,7 +124,7 @@ The upstream obsolete-member inventory found exactly seven public members whose | `RequireGovernancePolicy(RouteHandlerBuilder)` | `MarkGovernancePolicy()`, or `MarkGovernancePolicy(typeof(TPolicy))` for a plain marker | | `RequireGovernancePolicy(TBuilder, Type)` | `MarkGovernancePolicy(builder, policyType)` | -The non-obsolete `RequireGovernancePolicyAttribute` remains part of the implementation. Historical 4.x and 5.x migration material may retain old names when it is clearly presented as history; current examples must not present the removed members as callable 6.0 APIs. +The 5.x `RequireGovernancePolicyAttribute` type was renamed to `GovernancePolicyAttribute` in 6.0. That is a public type rename, not one of the seven obsolete-member removals above. Historical 4.x and 5.x migration material may retain old names when it is clearly presented as history; current examples must not present old names or removed members as 6.0 APIs. ## Decision Receipt Is Not Execution Proof @@ -136,6 +139,7 @@ Before publishing an API-facing Learning change: - label teaching code as illustrative or Learning-owned; - label exact framework syntax as AsiBackbone 6.0 API; - use finalized 6.0 names and namespaces; +- use `GovernancePolicyAttribute`, not the 5.x `RequireGovernancePolicyAttribute`, for attribute-based endpoint metadata; - do not call removed evaluator constructors or route-builder methods; - link implementation source to `release/6.0.0`, not `main` or a 5.x branch; - pin any future `AsiBackbone.*` sample package reference to a released 6.x version and commit its lock-file update; diff --git a/docs/getting-started/learning-1-asibackbone-6-compatibility.md b/docs/getting-started/learning-1-asibackbone-6-compatibility.md index c3c1e38..2ccda5c 100644 --- a/docs/getting-started/learning-1-asibackbone-6-compatibility.md +++ b/docs/getting-started/learning-1-asibackbone-6-compatibility.md @@ -75,6 +75,7 @@ Examples include: | `IAsiBackboneAuditSink` | `IDecisionReceiptSink` | | `IAsiBackboneEndpointGovernanceService` | `IEndpointGovernanceService` | | `IAsiBackboneAcknowledgmentChallengeService` | `IAcknowledgmentChallengeService` | +| `RequireGovernancePolicyAttribute` | `GovernancePolicyAttribute` | This is a representative teaching-oriented subset, not the complete rename inventory. Use the authoritative [6.0 public API naming convention](https://github.com/AsiBackbone/AsiBackbone/blob/release/6.0.0/docs/articles/public-api-naming-600.md) for the full list. @@ -111,7 +112,7 @@ AsiBackbone 6.0 removes exactly seven public members whose obsolete compatibilit - the five partial `DefaultAsiBackbonePolicyEvaluator` constructors are gone; use `DefaultGovernancePolicyEvaluator.CreateBuilder()` or the supported full-dependency constructor; - the two `RequireGovernancePolicy(...)` route-builder extension methods are gone; use `MarkGovernancePolicy(...)` instead. -The non-obsolete `RequireGovernancePolicyAttribute` remains supported. +The 5.x `RequireGovernancePolicyAttribute` type was also renamed to `GovernancePolicyAttribute` in 6.0 so the attribute and route-builder paths use the same marker terminology. That type rename is separate from the seven obsolete-member removals. Do not use this page as the complete implementation migration checklist. The authoritative [Upgrade from 5.x to 6.0](https://github.com/AsiBackbone/AsiBackbone/blob/release/6.0.0/docs/articles/upgrade-500-to-600.md) guide contains the exact removed-member inventory, replacement guidance, dependency-injection notes, and complete public type rename table. @@ -162,7 +163,7 @@ Older content is useful when read in version context. | --- | --- | | `AuditResidue` or **audit residue** in an older package example | Historical 5.x naming for what current Learning and the 6.0 API call a decision receipt. Preserve the old wording when discussing the historical release itself. | | `IAsiBackbone*`, `DefaultAsiBackbonePolicyEvaluator`, or other product-prefixed public types | Treat them as 5.x API names and translate them through the 6.0 naming and migration guides before copying code. | -| `RequireGovernancePolicy(...)` on a route builder | Treat it as historical 5.x syntax. Current 6.0 route metadata uses `MarkGovernancePolicy(...)`. | +| `RequireGovernancePolicy(...)` on a route builder or `RequireGovernancePolicyAttribute` on an endpoint | Treat them as historical 5.x names. Current 6.0 route metadata uses `MarkGovernancePolicy(...)`, and attribute-based metadata uses `GovernancePolicyAttribute`. | | A Learning sample that declares its own context, decision, receipt, acknowledgment, capability, or gateway type | Treat it as a teaching model unless the page explicitly labels the snippet as AsiBackbone 6.0 API. | | A historical release note, tag, or archived page | Read it as evidence of what that release taught or implemented at the time, not as the current production API contract. | | A conceptual statement about decision-before-execution, acknowledgment, scoped authority, or host-owned execution | Treat the concept as current unless a newer Learning page explicitly revises it. Verify package-specific behavior in AsiBackbone 6.0. | @@ -192,6 +193,7 @@ If you are moving a code example or internal document from an AsiBackbone 5.x ba - [ ] Replace 5.x package type names with the finalized 6.0 names where the snippet uses the real product API. - [ ] Replace removed partial evaluator constructors with the builder or full-dependency constructor. - [ ] Replace removed route-builder `RequireGovernancePolicy(...)` calls with `MarkGovernancePolicy(...)`. +- [ ] Replace 5.x `RequireGovernancePolicyAttribute` usage with `GovernancePolicyAttribute`. - [ ] Use **decision receipt** in current teaching prose while preserving historical wording in historical release material. - [ ] Use **acknowledgment** as the ordinary teaching term and reserve **handshake** for the actual protocol or exact retained type names. - [ ] Keep implementation links pinned to `release/6.0.0` when documenting the Learning 1.0 production baseline. diff --git a/tools/validate-asibackbone-6-api-references.cs b/tools/validate-asibackbone-6-api-references.cs index 40b8e20..b1ca2d3 100644 --- a/tools/validate-asibackbone-6-api-references.cs +++ b/tools/validate-asibackbone-6-api-references.cs @@ -7,11 +7,17 @@ return AsiBackboneApiReferenceValidator.Run(); -static class AsiBackboneApiReferenceValidator +static partial class AsiBackboneApiReferenceValidator { private const string ApiBoundaryRelativePath = "docs/getting-started/asibackbone-6-api-boundary.md"; + private static readonly HashSet HistoricalSymbolReferencePaths = new(StringComparer.Ordinal) + { + ApiBoundaryRelativePath, + "docs/getting-started/learning-1-asibackbone-6-compatibility.md" + }; + private static readonly HashSet ForbiddenCurrentSymbols = new(StringComparer.Ordinal) { "AsiBackboneAcknowledgmentChallenge", @@ -116,7 +122,8 @@ static class AsiBackboneApiReferenceValidator "IAsiBackboneSignatureVerificationService", "IAsiBackboneSigningService", "InMemoryAuditResidueLifecycleStore", - "RequireGovernancePolicy" + "RequireGovernancePolicy", + "RequireGovernancePolicyAttribute" }; private static readonly HashSet TextExtensions = new(StringComparer.OrdinalIgnoreCase) @@ -136,13 +143,15 @@ static class AsiBackboneApiReferenceValidator ".yml" }; - private static readonly Regex IdentifierRegex = new( + [GeneratedRegex( @"\b[A-Za-z_][A-Za-z0-9_]*\b", - RegexOptions.Compiled | RegexOptions.CultureInvariant); + RegexOptions.CultureInvariant)] + private static partial Regex IdentifierRegex(); - private static readonly Regex StaleImplementationLinkRegex = new( + [GeneratedRegex( @"https://github\.com/AsiBackbone/AsiBackbone/(?:blob|tree)/(?!release/6\.0\.0(?:/|\b))[^\s)\]'>]+", - RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)] + private static partial Regex StaleImplementationLinkRegex(); public static int Run() { @@ -159,7 +168,7 @@ public static int Run() } var errors = new List(); - IReadOnlyList textFiles = EnumerateTextFiles(repositoryRoot).ToArray(); + string[] textFiles = EnumerateTextFiles(repositoryRoot).ToArray(); ValidateCurrentSymbolsAndLinks(repositoryRoot, textFiles, errors); int packageReferenceCount = ValidatePackageReferences(repositoryRoot, errors); @@ -185,43 +194,46 @@ public static int Run() : $"{packageReferenceCount} AsiBackbone 6.x package reference(s)"; Console.WriteLine( - $"Validated AsiBackbone 6.0 API references across {textFiles.Count} instructional file(s): {packageSummary}."); + $"Validated AsiBackbone 6.0 API references across {textFiles.Length} instructional file(s): {packageSummary}."); return 0; } private static void ValidateCurrentSymbolsAndLinks( string repositoryRoot, IEnumerable files, - ICollection errors) + List errors) { foreach (string path in files) { string relativePath = NormalizeRelativePath(repositoryRoot, path); - if (string.Equals(relativePath, ApiBoundaryRelativePath, StringComparison.Ordinal) || - string.Equals(relativePath, "tools/validate-asibackbone-6-api-references.cs", StringComparison.Ordinal)) + if (string.Equals(relativePath, "tools/validate-asibackbone-6-api-references.cs", StringComparison.Ordinal)) { continue; } string text = File.ReadAllText(path); - string[] lines = File.ReadAllLines(path); - for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++) + if (!HistoricalSymbolReferencePaths.Contains(relativePath)) { - string line = lines[lineIndex]; + string[] lines = File.ReadAllLines(path); - foreach (Match identifierMatch in IdentifierRegex.Matches(line)) + for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++) { - if (ForbiddenCurrentSymbols.Contains(identifierMatch.Value)) + string line = lines[lineIndex]; + + foreach (Match identifierMatch in IdentifierRegex().Matches(line)) { - errors.Add( - $"{relativePath}:{lineIndex + 1} uses removed or renamed 5.x symbol '{identifierMatch.Value}'."); + if (ForbiddenCurrentSymbols.Contains(identifierMatch.Value)) + { + errors.Add( + $"{relativePath}:{lineIndex + 1} uses removed or renamed 5.x symbol '{identifierMatch.Value}'."); + } } } } - foreach (Match linkMatch in StaleImplementationLinkRegex.Matches(text)) + foreach (Match linkMatch in StaleImplementationLinkRegex().Matches(text)) { int lineNumber = GetLineNumber(text, linkMatch.Index); errors.Add( @@ -232,7 +244,7 @@ private static void ValidateCurrentSymbolsAndLinks( private static int ValidatePackageReferences( string repositoryRoot, - ICollection errors) + List errors) { var centralVersions = new Dictionary(StringComparer.OrdinalIgnoreCase); var references = new List(); @@ -318,7 +330,7 @@ private static int ValidatePackageReferences( private static void ValidateScopeNotices( string repositoryRoot, - ICollection errors) + List errors) { string[] noticePaths = {