fix(perf): name the measured profile in the 2.5D runner contract errors (#770)

Three profiles walk the 2.5D candidate contract of the large-house
runner - large-house-isometric-v1, its Stage 3 dense twin and the
backdrop twin of #743 - but both of its errors said
"large-house-isometric-v1 candidate has no ...", so a dense or backdrop
failure pointed at the wrong profile and job.

The two checks move into assertIsometricCandidate(card, profile, stage)
in demo/performance/card-contract.mjs, self-contained like
assertCardContract and injected the same way with toString(); the
runner calls it with the profile it measures for the pre-#448 Labs hook
and for the renderer contract. Conditions and wording are otherwise
unchanged, so a base or candidate that passed before passes now.

The unit test runs both the export and its serialized copy for all three
2.5D profiles and requires "<profile> candidate has no Labs fixture hook"
and "<profile> candidate has no renderer contract"; it also pins that the
runner injects and calls it and no longer names one profile for all. Red
on the previous runner (the anchor), and red when the message is
hardcoded back to large-house-isometric-v1 (dense profile).

Issue: #770
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
This commit is contained in:
Claude
2026-10-07 00:26:16 +00:00
committed by claude[bot]
parent 9906f68173
commit 283200c789
3 changed files with 62 additions and 7 deletions
+10 -7
View File
@@ -8,7 +8,11 @@ import { LARGE_HOUSE_COUNTS, makeLargeHouseFixture } from './fixtures/large-hous
import { assertFreshDemoBundle } from './bundle-freshness.mjs';
import { ensureHarnessEditorRuntime } from './editor-runtime-compat.mjs';
import { summarizeLongTasks, summarizeTimings } from './performance/evaluate.mjs';
import { assertCardContract, LARGE_HOUSE_CARD_CONTRACT } from './performance/card-contract.mjs';
import {
assertCardContract,
assertIsometricCandidate,
LARGE_HOUSE_CARD_CONTRACT,
} from './performance/card-contract.mjs';
import {
ISOMETRIC_STAGE3_DENSE_PROFILE,
makeIsometricStage3DenseFixture,
@@ -96,6 +100,9 @@ await page.addStyleTag({
await page.addScriptTag({
content: `window.__hpAssertCardContract = ${assertCardContract.toString()};`,
});
await page.addScriptTag({
content: `window.__hpAssertIsometricCandidate = ${assertIsometricCandidate.toString()};`,
});
await page.addScriptTag({
content: `window.__hpEnsureHarnessEditorRuntime = ${ensureHarnessEditorRuntime.toString()};`,
});
@@ -395,8 +402,7 @@ try {
const loadStarted = performance.now();
host.replaceChildren(card);
if (requiresIsometric && window.__hpAlpha !== true) {
if (typeof card._onLabsSnapshot !== 'function')
throw new Error('large-house-isometric-v1 candidate has no Labs fixture hook');
window.__hpAssertIsometricCandidate(card, profile, 'labs-hook');
// Comparison bundles before #448 do not understand hp_alpha. Preserve
// the cross-version benchmark only for that base; current candidates
// must activate through the real URL/storage contract above.
@@ -410,10 +416,7 @@ try {
throw new Error('large-house editor runtime did not preload');
if (isometric) await ensureIsoRuntime(card);
window.__hpAssertCardContract(card, cardContract);
if (requiresIsometric && (typeof card._effectiveProjection !== 'function'
|| !(card._isoGeometryCache instanceof Map))) {
throw new Error('large-house-isometric-v1 candidate has no renderer contract');
}
if (requiresIsometric) window.__hpAssertIsometricCandidate(card, profile, 'renderer');
await until(() => card._loadOk && card._model?.length === fixture.counts.floors);
await card.updateComplete;
await frame();
+20
View File
@@ -160,6 +160,26 @@ export const SPACE_GLOW_CARD_CONTRACT = Object.freeze({
}),
});
/**
* The 2.5D candidate contract of the large-house runner, shared by every 2.5D
* profile: `large-house-isometric-v1`, its Stage 3 dense twin and the backdrop
* twin (#743). The error names the profile being measured (#770); it used to
* name the historical profile whichever one failed. `labs-hook` is the
* pre-#448 activation path, checked before the first `hass`; `renderer` runs
* after the lazy renderer has loaded. Self-contained like
* `assertCardContract`: the runner serializes it with `toString()`.
*/
export function assertIsometricCandidate(card, profile, stage) {
if (stage === 'labs-hook') {
if (typeof card._onLabsSnapshot !== 'function')
throw new Error(`${profile} candidate has no Labs fixture hook`);
return;
}
if (stage !== 'renderer') throw new Error(`unknown 2.5D contract stage: ${stage}`);
if (typeof card._effectiveProjection !== 'function' || !(card._isoGeometryCache instanceof Map))
throw new Error(`${profile} candidate has no renderer contract`);
}
/** Single fail-fast implementation injected into both browser runners. Keep
* this function self-contained: runners serialize it with `toString()`. */
export function assertCardContract(card, contract) {
+32
View File
@@ -3,6 +3,7 @@ import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import {
assertCardContract,
assertIsometricCandidate,
GLOW_CARD_CONTRACT,
LARGE_HOUSE_CARD_CONTRACT,
SPACE_GLOW_CARD_CONTRACT,
@@ -164,6 +165,37 @@ test('large-house contract accepts only an explicit current or stable resize own
);
});
test('#770: 2.5D contract errors name the measured profile, not the historical one', () => {
const runner = readFileSync(new URL('../demo/benchmark_large_house.mjs', import.meta.url), 'utf8');
// Every 2.5D profile the runner accepts walks this contract.
const profiles = ['large-house-isometric-v1', 'isometric-stage3-dense-v1', 'large-house-isometric-backdrop-v1'];
// The runner injects the function with `toString()`: the serialized copy must
// behave exactly like the module export, so it is judged too.
const injected = new Function(`return ${assertIsometricCandidate.toString()}`)();
const complete = {
_onLabsSnapshot: () => undefined, _effectiveProjection: () => 'iso', _isoGeometryCache: new Map(),
};
for (const check of [assertIsometricCandidate, injected]) {
for (const profile of profiles) {
assert.throws(() => check({}, profile, 'labs-hook'),
(error) => error.message === `${profile} candidate has no Labs fixture hook`);
assert.throws(() => check({ ...complete, _isoGeometryCache: {} }, profile, 'renderer'),
(error) => error.message === `${profile} candidate has no renderer contract`);
assert.throws(() => check({ ...complete, _effectiveProjection: undefined }, profile, 'renderer'),
(error) => error.message === `${profile} candidate has no renderer contract`);
assert.doesNotThrow(() => check(complete, profile, 'labs-hook'));
assert.doesNotThrow(() => check(complete, profile, 'renderer'));
}
assert.throws(() => check(complete, 'isometric-stage3-dense-v1', 'renderr'), /unknown 2\.5D contract stage: renderr/);
}
// The runner injects it, calls both stages with the profile it measures and
// keeps no message of its own that names one profile for all three.
assert.match(runner, /window\.__hpAssertIsometricCandidate = \$\{assertIsometricCandidate\.toString\(\)\};/);
assert.match(runner, /window\.__hpAssertIsometricCandidate\(card, profile, 'labs-hook'\);/);
assert.match(runner, /if \(requiresIsometric\) window\.__hpAssertIsometricCandidate\(card, profile, 'renderer'\);/);
assert.doesNotMatch(runner, /large-house-isometric-v1 candidate has no/);
});
test('contract accepts recent optional fields only when their runtime type is valid', () => {
const base = {
_cleanFloorCache: new Map(), _glowClipCache: new Map(),