feat(m9/T32b): JSON Schema $ref resolver (lib seam, cycle/depth-guarded) + deploy-time dangling-ref block

This commit is contained in:
Joseph Doherty
2026-06-18 11:54:19 -04:00
parent 16cb078cd2
commit b3d99248fa
9 changed files with 755 additions and 14 deletions
@@ -22,6 +22,7 @@ public class FlatteningPipeline : IFlatteningPipeline
private readonly FlatteningService _flatteningService;
private readonly ValidationService _validationService;
private readonly RevisionHashService _revisionHashService;
private readonly ISharedSchemaRepository _sharedSchemaRepo;
/// <summary>Initializes a new <see cref="FlatteningPipeline"/> with the required template engine and site repositories and services.</summary>
/// <param name="templateRepo">Repository for loading templates and instance data.</param>
@@ -29,18 +30,25 @@ public class FlatteningPipeline : IFlatteningPipeline
/// <param name="flatteningService">Service that flattens the template inheritance chain into a resolved config.</param>
/// <param name="validationService">Service that performs semantic validation on the flattened config.</param>
/// <param name="revisionHashService">Service that computes the revision hash for staleness detection.</param>
/// <param name="sharedSchemaRepo">
/// M9-T32b: repository backing the JSON-Schema <c>$ref</c> resolution seam. Used to
/// look up <c>lib:Name</c> library references so a dangling reference in any validated
/// script schema becomes a deploy-blocking error.
/// </param>
public FlatteningPipeline(
ITemplateEngineRepository templateRepo,
ISiteRepository siteRepo,
FlatteningService flatteningService,
ValidationService validationService,
RevisionHashService revisionHashService)
RevisionHashService revisionHashService,
ISharedSchemaRepository sharedSchemaRepo)
{
_templateRepo = templateRepo;
_siteRepo = siteRepo;
_flatteningService = flatteningService;
_validationService = validationService;
_revisionHashService = revisionHashService;
_sharedSchemaRepo = sharedSchemaRepo;
}
/// <inheritdoc />
@@ -135,6 +143,15 @@ public class FlatteningPipeline : IFlatteningPipeline
.Select(c => c.Name)
.ToHashSet(StringComparer.Ordinal);
// M9-T32b: build the JSON-Schema $ref resolution seam from the shared-schema
// library. The seam ValidationService consumes is synchronous, so the library is
// pre-loaded once into a name→JSON map here (avoiding sync-over-async) and the
// seam is a pure in-memory lookup. An unresolved {"$ref":"lib:Name"} in any
// validated script schema then becomes a deploy-blocking SchemaReference error.
var sharedSchemas = await _sharedSchemaRepo.ListAsync(cancellationToken);
var schemaLibrary = sharedSchemas.ToDictionary(s => s.Name, s => s.SchemaJson, StringComparer.Ordinal);
Func<string, string?> resolveSchemaRef = name => schemaLibrary.GetValueOrDefault(name);
// Validate. This is the deploy-gating path, so connection-binding completeness
// is enforced as an Error (enforceConnectionBindings: true): a data-sourced
// attribute with no binding — or one bound to a connection that no longer exists
@@ -146,7 +163,8 @@ public class FlatteningPipeline : IFlatteningPipeline
resolvedSharedScripts,
alarmCapableConnectionNames,
enforceConnectionBindings: true,
siteConnectionNames: siteConnectionNames);
siteConnectionNames: siteConnectionNames,
resolveSchemaRef: resolveSchemaRef);
// Compute revision hash
var hash = _revisionHashService.ComputeHash(config);