From 15207dbacd7e52a885474b52025eb98bb8c8e383 Mon Sep 17 00:00:00 2001 From: Sergey Matyunin Date: Sun, 27 Sep 2026 10:01:45 +0300 Subject: [PATCH] =?UTF-8?q?test:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20Stage=206=20=D0=B2=20golden-=D0=BC=D0=B0=D1=82?= =?UTF-8?q?=D1=80=D0=B8=D1=86=D1=83=20(#673)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: #673 User-Visible: no --- demo/capture_stage6_acceptance_649.mjs | 5 +- demo/golden/matrix.mjs | 89 ++++++++++++------------ docs/design/649-25d-stage6/ACCEPTANCE.md | 11 ++- scripts/mutation-registry.mjs | 10 +++ test/golden-matrix.test.mjs | 36 +++++++++- 5 files changed, 97 insertions(+), 54 deletions(-) diff --git a/demo/capture_stage6_acceptance_649.mjs b/demo/capture_stage6_acceptance_649.mjs index d9ed7434..a71b4da7 100644 --- a/demo/capture_stage6_acceptance_649.mjs +++ b/demo/capture_stage6_acceptance_649.mjs @@ -1,6 +1,7 @@ // #649 AC12: product frames for docs/design/649-25d-stage6/ACCEPTANCE.md. -// Diagnostic, not golden (#455): run on a built bundle, compare by eye with the -// lab frames of sketch 07. +// These scenarios are canonical golden entries (#673). This focused command +// remains a diagnostic helper for rebuilding the side-by-side design document; +// baseline capture and acceptance still use the complete golden matrix (#455). // // npm run build && npm run bundle:sync // node demo/capture_stage6_acceptance_649.mjs [out-dir] diff --git a/demo/golden/matrix.mjs b/demo/golden/matrix.mjs index 7e9e1836..30c1b8f4 100644 --- a/demo/golden/matrix.mjs +++ b/demo/golden/matrix.mjs @@ -1,7 +1,7 @@ import { fixtureWallKey } from '../fixtures/visual-matrix.mjs'; /** Data-only HP-QA-01 capture matrix. Bump when framing or scenarios change. */ -export const GOLDEN_MATRIX_VERSION = 65; +export const GOLDEN_MATRIX_VERSION = 66; const stage = { capture: 'stage', threshold: { maxChannelDelta: 10, maxDiffRatio: 0.0005 } }; const page = { capture: 'page', threshold: { maxChannelDelta: 10, maxDiffRatio: 0.0008 } }; @@ -218,7 +218,50 @@ const stairLayerFixture = [ target_space_id: 'golden-lighting' }, ]; +/** + * #649/#673 Stage 6 acceptance scenes + * (docs/design/649-25d-stage6/ACCEPTANCE.md): tiles, floor shadows, soft sun + * wash and theme-free walls in the four theme × floor combinations of the + * approved sketch 07 (attachments 09, 10, 12, 13) plus the lifted hover frame + * (attachment 11); sun as the lab default 10:00. These exact objects are part + * of GOLDEN_SCENARIOS below; the separate export keeps the focused diagnostic + * side-by-side capture script useful without defining a second matrix. + */ +export const STAGE6_ACCEPTANCE_SCENARIOS = Object.freeze([ + ...[['light', 'light'], ['light', 'dark'], ['dark', 'dark'], ['dark', 'light']].map(([theme, floor]) => ({ + id: `isometric-stage6-${theme}-${floor}floor`, fixture: 'visual', + space: 'golden-lighting', mode: 'view', projection: 'iso', + stage3Fixture: { roomMetrics: true, lockOpening: 'light-door', lockState: 'locked' }, + stage3Golden: { requiredKinds: stage3RequiredOverlays }, + markerOverrides: stage3DenseMarkers, + layoutOverrides: stage3DenseLayout, + stateOverrides: { + 'light.golden_light_one': { attributes: { lqi: 40 } }, + 'light.golden_light_two': { attributes: { lqi: 120 } }, + 'sensor.golden_right_linkquality': { state: '210' }, + 'sun.sun': { attributes: { azimuth: 150, elevation: 52 } }, + }, + fillMode: 'custom', customFill: { c: floor === 'light' ? '#eee8de' : '#737777', a: 1 }, + glowEnabled: false, sunRays: true, northDeg: 180, + // The lab's walls are the default white; the colour is the user's either way. + wallFill: { c: '#ffffff', a: 1 }, + theme, viewport: { width: 1000, height: 900 }, ...stage, + })), + { id: 'isometric-stage6-hover-light', fixture: 'visual', + space: 'golden-lighting', mode: 'view', projection: 'iso', + stage3Fixture: { roomMetrics: true, lockOpening: 'light-door', lockState: 'locked' }, + stage3Golden: { requiredKinds: stage3RequiredOverlays }, + markerOverrides: stage3DenseMarkers, + layoutOverrides: stage3DenseLayout, + fillMode: 'custom', customFill: { c: '#eee8de', a: 1 }, + glowEnabled: false, sunRays: false, + hoverDevice: 'golden-light-two', + theme: 'light', viewport: { width: 1000, height: 900 }, ...stage, + }, +]); + export const GOLDEN_SCENARIOS = Object.freeze([ + ...STAGE6_ACCEPTANCE_SCENARIOS, // #663 AC3/AC10/AC12: both stair geometries, both rise directions and // small/large footprints across themes, flat/2.5D and interaction states. { id: 'stairs-flat-normal-light', fixture: 'visual', space: 'golden-geometry', mode: 'view', @@ -1224,47 +1267,3 @@ export const GOLDEN_SCENARIOS = Object.freeze([ cardEditorInvalidDefaultFloor: 'removed-floor', language: 'ru', theme: 'dark', viewport: { width: 900, height: 760 }, ...page }, ]); - -/** - * #649 Stage 6 acceptance scenes (docs/design/649-25d-stage6/ACCEPTANCE.md): - * tiles, floor shadows, soft sun wash and theme-free walls in the four - * theme × floor combinations of the approved sketch 07 (attachments 09, 10, - * 12, 13) plus the lifted hover frame (attachment 11); sun as the lab default - * 10:00. They join GOLDEN_SCENARIOS in the same class D commit as their - * baselines captured on Linux CI (#455): every matrix scene needs a reviewed - * baseline (test/golden-wsl-artifact.test.mjs, #641), and a baseline cannot be - * captured before the scene exists. Until then they drive the diagnostic - * side-by-side frames only. - */ -export const STAGE6_ACCEPTANCE_SCENARIOS = Object.freeze([ - ...[['light', 'light'], ['light', 'dark'], ['dark', 'dark'], ['dark', 'light']].map(([theme, floor]) => ({ - id: `isometric-stage6-${theme}-${floor}floor`, fixture: 'visual', - space: 'golden-lighting', mode: 'view', projection: 'iso', - stage3Fixture: { roomMetrics: true, lockOpening: 'light-door', lockState: 'locked' }, - stage3Golden: { requiredKinds: stage3RequiredOverlays }, - markerOverrides: stage3DenseMarkers, - layoutOverrides: stage3DenseLayout, - stateOverrides: { - 'light.golden_light_one': { attributes: { lqi: 40 } }, - 'light.golden_light_two': { attributes: { lqi: 120 } }, - 'sensor.golden_right_linkquality': { state: '210' }, - 'sun.sun': { attributes: { azimuth: 150, elevation: 52 } }, - }, - fillMode: 'custom', customFill: { c: floor === 'light' ? '#eee8de' : '#737777', a: 1 }, - glowEnabled: false, sunRays: true, northDeg: 180, - // The lab's walls are the default white; the colour is the user's either way. - wallFill: { c: '#ffffff', a: 1 }, - theme, viewport: { width: 1000, height: 900 }, ...stage, - })), - { id: 'isometric-stage6-hover-light', fixture: 'visual', - space: 'golden-lighting', mode: 'view', projection: 'iso', - stage3Fixture: { roomMetrics: true, lockOpening: 'light-door', lockState: 'locked' }, - stage3Golden: { requiredKinds: stage3RequiredOverlays }, - markerOverrides: stage3DenseMarkers, - layoutOverrides: stage3DenseLayout, - fillMode: 'custom', customFill: { c: '#eee8de', a: 1 }, - glowEnabled: false, sunRays: false, - hoverDevice: 'golden-light-two', - theme: 'light', viewport: { width: 1000, height: 900 }, ...stage, - }, -]); diff --git a/docs/design/649-25d-stage6/ACCEPTANCE.md b/docs/design/649-25d-stage6/ACCEPTANCE.md index d3ae38fd..c3dea619 100644 --- a/docs/design/649-25d-stage6/ACCEPTANCE.md +++ b/docs/design/649-25d-stage6/ACCEPTANCE.md @@ -21,12 +21,11 @@ issue body; implementation notes: `docs/ISOMETRIC.md` § Stage 6, `docs/SUN.md` `node demo/capture_stage6_acceptance_649.mjs` on a built bundle. The furniture and settings frames come from the demo fixture. -These are **diagnostic** frames, not golden baselines. The five scenes join -`GOLDEN_SCENARIOS` together with their baselines captured and accepted on -Linux CI (#455) — a matrix scene without a reviewed baseline turns -`test/golden-wsl-artifact.test.mjs` (#641) red, and a baseline cannot exist -before its scene. PNGs are -reduced to a 256-colour palette; that is enough to compare layout and tone. +The five product scenes are canonical entries of `GOLDEN_SCENARIOS` (#673), +with reviewed baselines captured and accepted through the Linux/WSL contract +(#455, #641). The side-by-side PNGs in this document remain diagnostic copies: +they are reduced to a 256-colour palette, which is enough to compare layout and +tone, while the golden baselines retain their canonical bytes. The two plans differ (the lab is its own SVG snapshot of a first floor; the product frame is the golden lighting fixture), so the comparison is of the diff --git a/scripts/mutation-registry.mjs b/scripts/mutation-registry.mjs index 9dfba4b4..335134df 100644 --- a/scripts/mutation-registry.mjs +++ b/scripts/mutation-registry.mjs @@ -10792,6 +10792,16 @@ const MUTANT_DEFINITIONS = [ replace: ' rotDeg: 4,\n', }], }, + { + id: 'stage6-acceptance-left-diagnostic-only', + guard: 'node --test --test-name-pattern="#673" test/golden-matrix.test.mjs', + because: '#673: the five approved Stage 6 frames must be in the canonical matrix, not only the diagnostic exporter', + patches: [{ + file: 'demo/golden/matrix.mjs', + find: ' ...STAGE6_ACCEPTANCE_SCENARIOS,\n // #663 AC3/AC10/AC12:', + replace: ' // mutant: Stage 6 acceptance remains diagnostic-only\n // #663 AC3/AC10/AC12:', + }], + }, { id: 'stage3-w2-room-label-left-on-floor', guard: 'node --test --test-name-pattern="exact Stage 4 overlay matrix" test/iso-overlays.test.mjs', diff --git a/test/golden-matrix.test.mjs b/test/golden-matrix.test.mjs index b51fa15b..a666ef07 100644 --- a/test/golden-matrix.test.mjs +++ b/test/golden-matrix.test.mjs @@ -4,6 +4,7 @@ import test from 'node:test'; import { prepareGoldenFixture } from '../demo/golden/harness.mjs'; import { GOLDEN_MATRIX_VERSION, GOLDEN_SCENARIOS, OPENING_SYMBOL_EXISTING_GOLDEN_IMPACT, + STAGE6_ACCEPTANCE_SCENARIOS, } from '../demo/golden/matrix.mjs'; import { fixtureWallKey } from '../demo/fixtures/visual-matrix.mjs'; import { readHouseplanProductionSource } from './houseplan-source.mjs'; @@ -445,7 +446,7 @@ test('sun-ray golden requires browser-painted light from a state-only sun entity assert.ok(scenario); const fixture = prepareGoldenFixture(scenario); const space = fixture.config.spaces.find((item) => item.id === scenario.space); - assert.equal(GOLDEN_MATRIX_VERSION, 65); + assert.equal(GOLDEN_MATRIX_VERSION, 66); assert.equal(space.settings.sun_rays, true); assert.equal(scenario.northDeg, 90, 'the sign-sensitive golden must keep a non-zero north direction'); @@ -941,6 +942,39 @@ test('issue 570 Stage 4 reuses the historical iso goldens for visual handoff cov 'combined live-layer preflight must run after the async vacuum fixture settles'); }); +test('#673 Stage 6 designer acceptance scenes are canonical golden entries', () => { + const ids = STAGE6_ACCEPTANCE_SCENARIOS.map((scenario) => scenario.id); + assert.equal(GOLDEN_MATRIX_VERSION, 66); + assert.deepEqual(ids, [ + 'isometric-stage6-light-lightfloor', + 'isometric-stage6-light-darkfloor', + 'isometric-stage6-dark-darkfloor', + 'isometric-stage6-dark-lightfloor', + 'isometric-stage6-hover-light', + ]); + for (const scenario of STAGE6_ACCEPTANCE_SCENARIOS) { + assert.strictEqual(GOLDEN_SCENARIOS.find((item) => item.id === scenario.id), scenario, + `${scenario.id} must be the exact reviewed matrix object, not a drifting copy`); + assert.equal(scenario.projection, 'iso'); + assert.equal(scenario.mode, 'view'); + assert.equal(scenario.capture, 'stage'); + assert.deepEqual(scenario.stage3Golden.requiredKinds, + ['device', 'room-label', 'opening-lock']); + } + const themeFloor = STAGE6_ACCEPTANCE_SCENARIOS.slice(0, 4); + assert.deepEqual(themeFloor.map((scenario) => [scenario.theme, scenario.customFill.c]), [ + ['light', '#eee8de'], ['light', '#737777'], + ['dark', '#737777'], ['dark', '#eee8de'], + ]); + assert.equal(themeFloor.every((scenario) => scenario.sunRays === true + && scenario.northDeg === 180 + && scenario.stateOverrides['sun.sun'].attributes.azimuth === 150 + && scenario.stateOverrides['sun.sun'].attributes.elevation === 52), true); + const hover = STAGE6_ACCEPTANCE_SCENARIOS[4]; + assert.equal(hover.hoverDevice, 'golden-light-two'); + assert.equal(hover.sunRays, false); +}); + test('golden harness applies doorway, state and layout overrides to a cloned fixture', () => { const scenario = GOLDEN_SCENARIOS.find((item) => item.id === 'lighting-opaque-glow-two-doorways-dark'); const fixture = prepareGoldenFixture(scenario);