Skip to content

Commit 8acae67

Browse files
committed
Fix website qualification for original-artwork 3D scene
1 parent aeb3fc2 commit 8acae67

12 files changed

Lines changed: 156 additions & 251 deletions

‎docs/ADR/ADR-040-static-site-threejs-evidence.md‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,11 @@ fallback inventory and matching assertions through a source revert while preserv
217217
unrelated work, immutable original evidence and all publication/coverage gates.
218218
It does not retain a second renderer or use a still image as the live scene.
219219

220-
Current stage is the source contract only: implementation, meaningful native
221-
browser verification, owner approval and delivery remain pending. The Feature
222-
records earlier local evidence and the four unrelated IntegrationTests errors
223-
from the prior full-solution attempt; those are not acceptance of this composition.
220+
Current stage: native implementation and the exact original-artwork fallback are
221+
present. The 2026-10-09 owner-requested Website build repair aligns the existing
222+
asset and browser assertions with this composition while preserving motion,
223+
fallback, accessibility, coverage and publication gates. Manual browser evidence
224+
and successful scoped compilation/static building are recorded in the Feature;
225+
exact-source Linux qualification, owner visual approval and delivery remain
226+
pending. Earlier local results do not qualify this composition or current database
227+
source.

‎docs/Features/BenchmarkComparisons.md‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,18 @@ flowchart LR
6161
Review --> Delivery[Approval then scoped commit and Website gates]
6262
```
6363

64-
Current PRESENTATION-005 stage: contract established; native source, fallback,
65-
meaningful browser qualification and owner review are pending. The earlier
66-
five-case/4,731-triangle local result below qualifies only that previous working-tree
67-
iteration, not this new composition. The previous full solution build failed on
68-
four unrelated IntegrationTests errors; no new solution pass, visual acceptance,
69-
commit/push or Pages receipt is claimed by this specification update.
64+
Current PRESENTATION-005 stage: the native scene and original-artwork fallback are
65+
implemented. Manual in-app-browser review observed eight model platforms, one
66+
agent, eight links, 117,027 triangles and nine draw calls. The owner-directed
67+
Website build repair on 2026-10-09 updates the existing asset/browser oracles to
68+
this composition: the exact PNG replaces the retired SVGs, the canvas replaces
69+
the removed projected mark, and renderer limits use the approved 120,000-triangle
70+
bound. Motion, fallback, accessibility, coverage and publication gates remain
71+
mandatory. The static content-only build and SiteTests Release compilation pass
72+
locally with zero compiler warnings/errors; no local tests were run for this
73+
repair. Exact-source Linux Website/Pages qualification and owner visual approval
74+
remain pending. The earlier five-case/4,731-triangle result below qualifies only
75+
that previous iteration, not this composition or the current database source.
7076

7177
Earlier TASK-BC-PRESENTATION-001..003 mapped respectively to the same-numbered REQ/AC:
7278
establish the current content-only build/browser baseline; update HTML/CSS,

‎tests/KeyLoad.SiteTests/Features/BenchmarkComparisons/Assertions/SiteBrowserVectorAssetAssertions.cs‎

Lines changed: 47 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -11,77 +11,56 @@ await Assert.That(await cdp.WaitForExpressionAsync(SiteBrowserSceneTokens.GraphR
1111
var state = await ReadMarkAsync(cdp, cancellationToken);
1212
await Assert.That(state.GetProperty(SiteVectorAssetTokens.CountField).GetInt32()).IsEqualTo(SiteTokens.One);
1313
await Assert.That(state.GetProperty(SiteVectorAssetTokens.LoadedField).GetBoolean()).IsTrue();
14-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.SourceField).GetString()).IsEqualTo(SiteVectorAssetTokens.ExpectedFaviconPath);
15-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.DisplayField).GetString()).IsNotEqualTo(SiteVectorAssetTokens.CssNone);
16-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.VisibilityField).GetString()).IsNotEqualTo(SiteVectorAssetTokens.HiddenVisibility);
17-
await Assert.That(CssNumber(state.GetProperty(SiteVectorAssetTokens.OpacityField).GetString()))
18-
.IsGreaterThan(SiteVectorAssetTokens.PositiveSize);
19-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.PosterVisibilityField).GetString()).IsEqualTo(SiteVectorAssetTokens.HiddenVisibility);
14+
await Assert.That(state.GetProperty(SiteVectorAssetTokens.MarkCountField).GetInt32()).IsEqualTo(SiteTokens.Zero);
15+
await Assert.That(state.GetProperty(SiteVectorAssetTokens.PosterVisibilityField).GetString())
16+
.IsEqualTo(SiteVectorAssetTokens.HiddenVisibility);
17+
await Assert.That(state.GetProperty(SiteVectorAssetTokens.TransformField).GetString() is { Length: > 0 }).IsTrue();
2018
await AssertClusterGraphAsync(cdp, cancellationToken);
21-
await AssertProjectedDimensions(state);
22-
var transform = state.GetProperty(SiteVectorAssetTokens.TransformField).GetString() ?? string.Empty;
23-
await Assert.That(transform.StartsWith(SiteVectorAssetTokens.Matrix3dPrefix, StringComparison.Ordinal)).IsTrue();
24-
await Assert.That(transform.EndsWith(SiteVectorAssetTokens.MatrixClosingCharacter)).IsTrue();
19+
await AssertCanvasDimensionsAsync(state);
2520
}
2621

2722
private static async Task AssertClusterGraphAsync(SiteBrowserCdpClient cdp, CancellationToken token)
2823
{
2924
var graph = await cdp.EvaluateAsync(SiteBrowserSceneTokens.ClusterGraphScript, false, token);
30-
await Assert.That(graph.GetProperty(SiteBrowserSceneTokens.SilosField).GetInt32()).IsEqualTo(3);
31-
await Assert.That(graph.GetProperty(SiteBrowserSceneTokens.GrainsField).GetInt32()).IsEqualTo(18);
32-
await Assert.That(graph.GetProperty(SiteBrowserSceneTokens.LinksField).GetInt32()).IsEqualTo(33);
33-
await Assert.That(graph.GetProperty(SiteBrowserSceneTokens.ClientsField).GetInt32()).IsEqualTo(3);
34-
await AssertGraphLabelsAsync(graph.GetProperty(SiteBrowserSceneTokens.ModelLabelsField),
35-
SiteBrowserSceneTokens.ModelNames);
36-
await AssertGraphLabelsAsync(graph.GetProperty(SiteBrowserSceneTokens.ClientLabelsField),
37-
SiteBrowserSceneTokens.ClientNames);
38-
var description = graph.GetProperty(SiteBrowserSceneTokens.DescriptionField).GetString()!;
39-
await Assert.That(description.Contains("Orleans", StringComparison.OrdinalIgnoreCase)).IsTrue();
40-
await Assert.That(description.Contains("conceptual", StringComparison.OrdinalIgnoreCase)).IsTrue();
41-
var labels = graph.GetProperty(SiteBrowserSceneTokens.LabelsField).EnumerateArray().ToArray();
42-
await Assert.That(labels.Length).IsEqualTo(3);
43-
for (var index = 0; index < labels.Length; index++)
44-
{
45-
var label = labels[index];
46-
var text = label.GetProperty(SiteBrowserSceneTokens.TextField).GetString()!;
47-
var name = SiteBrowserSceneTokens.SiloNames[index];
48-
await Assert.That(text.Contains("Silo " + name, StringComparison.Ordinal)).IsTrue();
49-
await Assert.That(text.Contains("Node " + name, StringComparison.Ordinal)).IsTrue();
50-
await Assert.That(text.Contains("Grain activations", StringComparison.Ordinal)).IsTrue();
51-
await Assert.That(text.Contains("Node-local storage", StringComparison.Ordinal)).IsTrue();
52-
await Assert.That(label.GetProperty(SiteBrowserSceneTokens.VisibleField).GetBoolean()).IsTrue();
53-
await Assert.That(label.GetProperty(SiteBrowserSceneTokens.ContainedField).GetBoolean()).IsTrue();
54-
}
55-
}
56-
57-
private static async Task AssertGraphLabelsAsync(JsonElement labels, string[] expectedNames)
58-
{
59-
var entries = labels.EnumerateArray().ToArray();
60-
await Assert.That(entries.Length).IsEqualTo(expectedNames.Length);
61-
foreach (var expected in expectedNames)
25+
await Assert.That(graph.GetProperty(SiteBrowserSceneTokens.ModelsField).GetInt32()).IsEqualTo(SiteBrowserSceneTokens.ModelCount);
26+
await Assert.That(graph.GetProperty(SiteBrowserSceneTokens.AgentsField).GetInt32()).IsEqualTo(SiteBrowserSceneTokens.AgentCount);
27+
await Assert.That(graph.GetProperty(SiteBrowserSceneTokens.LinksField).GetInt32()).IsEqualTo(SiteBrowserSceneTokens.LinkCount);
28+
await Assert.That(graph.GetProperty(SiteBrowserSceneTokens.ClientsField).GetInt32()).IsEqualTo(SiteTokens.Zero);
29+
await Assert.That(graph.GetProperty(SiteBrowserSceneTokens.MarksField).GetInt32()).IsEqualTo(SiteTokens.Zero);
30+
await Assert.That(graph.GetProperty(SiteBrowserSceneTokens.CaptionsField).GetInt32()).IsEqualTo(SiteTokens.Zero);
31+
var entries = graph.GetProperty(SiteBrowserSceneTokens.ModelLabelsField).EnumerateArray().ToArray();
32+
await Assert.That(entries.Length).IsEqualTo(SiteBrowserSceneTokens.ModelNames.Length);
33+
foreach (var expected in SiteBrowserSceneTokens.ModelNames)
6234
{
6335
await Assert.That(entries.Count(label => string.Equals(label.GetProperty(SiteBrowserSceneTokens.TextField).GetString(),
6436
expected, StringComparison.Ordinal))).IsEqualTo(SiteTokens.One);
6537
}
66-
6738
foreach (var label in entries)
6839
{
69-
await Assert.That(label.GetProperty(SiteBrowserSceneTokens.VisibleField).GetBoolean()).IsTrue();
70-
await Assert.That(label.GetProperty(SiteBrowserSceneTokens.ContainedField).GetBoolean()).IsTrue();
40+
await Assert.That(label.GetProperty(SiteBrowserSceneTokens.AccessibleField).GetBoolean()).IsTrue();
7141
}
42+
await AssertDescriptionAsync(graph);
43+
}
44+
45+
private static async Task AssertDescriptionAsync(JsonElement state)
46+
{
47+
var description = state.GetProperty(SiteBrowserSceneTokens.DescriptionField).GetString()!;
48+
await Assert.That(description.Contains(SiteVectorAssetTokens.AgentDescription, StringComparison.OrdinalIgnoreCase)).IsTrue();
49+
await Assert.That(description.Contains(SiteVectorAssetTokens.DatabaseDescription, StringComparison.OrdinalIgnoreCase)).IsTrue();
50+
await Assert.That(description.Contains(SiteVectorAssetTokens.ConceptualDescription, StringComparison.OrdinalIgnoreCase)).IsTrue();
7251
}
7352

7453
public static async Task AssertPosterFallbackAsync(SiteBrowserCdpClient cdp,
7554
CancellationToken cancellationToken)
7655
{
7756
var state = await cdp.EvaluateAsync(SiteVectorAssetTokens.PosterStateScript, false, cancellationToken);
7857
await Assert.That(state.GetProperty(SiteVectorAssetTokens.PosterVisibleField).GetBoolean()).IsTrue();
79-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.MarkVisibleField).GetBoolean()).IsFalse();
80-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.MarkCountField).GetInt32()).IsEqualTo(SiteTokens.One);
58+
await Assert.That(state.GetProperty(SiteVectorAssetTokens.LoadedField).GetBoolean()).IsTrue();
59+
await Assert.That(state.GetProperty(SiteVectorAssetTokens.SourceField).GetString()!
60+
.EndsWith(SiteContentTokens.DesktopPosterSuffix, StringComparison.Ordinal)).IsTrue();
61+
await Assert.That(state.GetProperty(SiteVectorAssetTokens.MarkCountField).GetInt32()).IsEqualTo(SiteTokens.Zero);
8162
await Assert.That(state.GetProperty(SiteBrowserSceneTokens.LabelsHiddenField).GetBoolean()).IsTrue();
82-
var description = state.GetProperty(SiteBrowserSceneTokens.DescriptionField).GetString()!;
83-
await Assert.That(description.Contains("Orleans", StringComparison.OrdinalIgnoreCase)).IsTrue();
84-
await Assert.That(description.Contains("conceptual", StringComparison.OrdinalIgnoreCase)).IsTrue();
63+
await AssertDescriptionAsync(state);
8564
}
8665

8766
public static async Task<JsonElement> ReadMarkAsync(SiteBrowserCdpClient cdp,
@@ -99,7 +78,7 @@ await Assert.That(current.GetProperty(SiteVectorAssetTokens.TransformField).GetS
9978

10079
public static async Task AssertRetinaResizeAsync(SiteBrowserCdpClient cdp, CancellationToken cancellationToken)
10180
{
102-
var originalMark = await ReadMarkAsync(cdp, cancellationToken);
81+
var previous = await ReadMarkAsync(cdp, cancellationToken);
10382
foreach (var width in SiteBrowserUiTokens.ViewportWidths)
10483
{
10584
await cdp.CommandAsync(SiteBrowserTokens.SetDeviceMetrics, new Dictionary<string, object?>
@@ -112,41 +91,26 @@ public static async Task AssertRetinaResizeAsync(SiteBrowserCdpClient cdp, Cance
11291
await cdp.EvaluateAsync(SiteBrowserUiTokens.SceneScrollScript, false, cancellationToken);
11392
await Assert.That(await cdp.WaitForExpressionAsync(SiteVectorAssetTokens.ReadyPredicate, cancellationToken)).IsTrue();
11493
await Task.Delay(TimeSpan.FromMilliseconds(SiteBrowserUiTokens.SceneSettleWaitMilliseconds), TimeProvider.System, cancellationToken);
115-
await AssertPoseUpdatedAsync(cdp, originalMark, cancellationToken);
116-
originalMark = await ReadMarkAsync(cdp, cancellationToken);
94+
await AssertReadyMarkAsync(cdp, cancellationToken);
95+
var current = await ReadMarkAsync(cdp, cancellationToken);
96+
await Assert.That(current.GetProperty(SiteVectorAssetTokens.WidthField).GetDouble())
97+
.IsNotEqualTo(previous.GetProperty(SiteVectorAssetTokens.WidthField).GetDouble());
98+
previous = current;
11799
}
118100
}
119101

120-
private static async Task AssertProjectedDimensions(JsonElement state)
121-
{
122-
var width = CssPixels(state.GetProperty(SiteVectorAssetTokens.WidthField).GetString());
123-
var height = CssPixels(state.GetProperty(SiteVectorAssetTokens.HeightField).GetString());
124-
await Assert.That(width > SiteVectorAssetTokens.PositiveSize && height > SiteVectorAssetTokens.PositiveSize).IsTrue();
125-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.OffsetWidthField).GetDouble()).IsEqualTo(width).Within(SiteVectorAssetTokens.CssDimensionTolerance);
126-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.OffsetHeightField).GetDouble()).IsEqualTo(height).Within(SiteVectorAssetTokens.CssDimensionTolerance);
127-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.RectWidthField).GetDouble() > SiteVectorAssetTokens.PositiveSize).IsTrue();
128-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.RectHeightField).GetDouble() > SiteVectorAssetTokens.PositiveSize).IsTrue();
129-
await AssertContainedRasterSize(state, width, height);
130-
}
131-
132-
private static async Task AssertContainedRasterSize(JsonElement state, double width, double height)
102+
private static async Task AssertCanvasDimensionsAsync(JsonElement state)
133103
{
134-
var dimensions = state.GetProperty(SiteVectorAssetTokens.BoundsField);
135-
await Assert.That(dimensions.GetProperty(SiteVectorAssetTokens.ContainedField).GetBoolean()).IsTrue();
136-
await Assert.That(dimensions.GetProperty(SiteVectorAssetTokens.CenteredField).GetBoolean()).IsTrue();
137-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.RectWidthField).GetDouble() / width)
138-
.IsBetween(SiteVectorAssetTokens.MinimumProjectionRatio, SiteVectorAssetTokens.MaximumProjectionRatio);
139-
await Assert.That(state.GetProperty(SiteVectorAssetTokens.RectHeightField).GetDouble() / height)
140-
.IsBetween(SiteVectorAssetTokens.MinimumProjectionRatio, SiteVectorAssetTokens.MaximumProjectionRatio);
104+
await Assert.That(state.GetProperty(SiteVectorAssetTokens.WidthField).GetDouble()).IsGreaterThan(SiteTokens.Zero);
105+
await Assert.That(state.GetProperty(SiteVectorAssetTokens.HeightField).GetDouble()).IsGreaterThan(SiteTokens.Zero);
106+
await Assert.That(state.GetProperty(SiteVectorAssetTokens.ContainedField).GetBoolean()).IsTrue();
107+
await Assert.That(state.GetProperty(SiteBrowserUiTokens.BufferPixelsField).GetInt32())
108+
.IsBetween(SiteTokens.One, SiteBrowserUiTokens.RendererPixelLimit);
109+
await Assert.That(state.GetProperty(SiteBrowserUiTokens.PixelRatioField).GetDouble())
110+
.IsLessThanOrEqualTo(SiteBrowserUiTokens.RendererPixelRatioLimit);
111+
await Assert.That(state.GetProperty(SiteBrowserUiTokens.TrianglesField).GetInt32())
112+
.IsBetween(SiteTokens.One, SiteBrowserUiTokens.RendererTriangleLimit);
113+
await Assert.That(state.GetProperty(SiteBrowserUiTokens.DrawCallsField).GetInt32())
114+
.IsBetween(SiteTokens.One, SiteBrowserUiTokens.RendererDrawCallLimit);
141115
}
142-
143-
private static double CssPixels(string? value) => value is not null &&
144-
value.EndsWith(SiteVectorAssetTokens.CssPixelSuffix, StringComparison.Ordinal) &&
145-
double.TryParse(value[..^SiteVectorAssetTokens.CssPixelSuffix.Length],
146-
System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var pixels)
147-
? pixels : SiteTokens.Zero;
148-
149-
private static double CssNumber(string? value) => double.TryParse(value,
150-
System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var number)
151-
? number : SiteTokens.Zero;
152116
}

‎tests/KeyLoad.SiteTests/Features/BenchmarkComparisons/Cases/SiteVectorAssetBrowserTests.cs‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,26 @@ namespace KeyLoad.SiteTests.Features.BenchmarkComparisons;
55
internal sealed class SiteVectorAssetBrowserTests
66
{
77
[Test]
8-
public async Task AC_VEC_003_RealFavicon404KeepsTheVectorPosterAndNeverBecomesReady()
8+
public async Task AC_VEC_003_RealRendererModule404KeepsOriginalArtworkAndNeverBecomesReady()
99
{
10-
var fixture = await SiteIsolatedFixture.ReadAsync();
10+
var inputs = SiteContentInputs.FromEnvironment();
1111
var token = TestContext.Current!.Execution.CancellationToken;
12-
await using var browser = await SiteIsolatedBrowserStartup.StartAsync(fixture, token);
12+
await using var temporary = SiteTempDirectory.Create();
13+
var build = await SiteContentBuilderProcess.BuildAsync(inputs, temporary.Output, token);
14+
await Assert.That(build.ExitCode).IsEqualTo(SiteTokens.ProcessSuccessExitCode);
15+
await Assert.That(build.StandardError.Length).IsEqualTo(SiteTokens.Zero);
16+
await using var browser = await SiteContentBrowserStartup.StartAsync(inputs, temporary.Output, token);
1317
var cdp = browser.Chrome.Cdp;
1418
await browser.Chrome.NavigateAsync(SiteBrowserTokens.BlankUrl, token);
15-
File.Delete(Path.Combine(browser.Output, SiteAssetTokens.FaviconSvg));
19+
var missingModule = Path.Combine(SiteAssetTokens.EmittedFeatureRelativePath,
20+
SiteAssetTokens.ThreeVendorRelativePath, SiteAssetTokens.WebGpuVendorModule);
21+
File.Delete(Path.Combine(temporary.Output, missingModule));
1622
await cdp.CommandAsync(SiteVectorAssetTokens.DisableCacheMethod, new Dictionary<string, object?>
1723
{
1824
[SiteVectorAssetTokens.CacheDisabledField] = true,
1925
}, token);
2026
using var http = new HttpClient { Timeout = TimeSpan.FromMilliseconds(SiteBrowserTokens.BrowserCommandTimeoutMilliseconds) };
21-
using var response = await http.GetAsync(new Uri(browser.BaseUrl + SiteAssetTokens.FaviconSvg), token);
27+
using var response = await http.GetAsync(new Uri(browser.BaseUrl + missingModule.Replace(Path.DirectorySeparatorChar, '/')), token);
2228
await Assert.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound);
2329
await browser.Chrome.NavigateAsync(browser.BaseUrl + SiteAssetTokens.IndexHtml, token);
2430
await cdp.EvaluateAsync(SiteBrowserUiTokens.SceneScrollScript, false, token);

0 commit comments

Comments
 (0)